CS 153: Project 7

Title image Fall 2018

Project 7: Shaky Lights

The purpose of this project is to continue to practice creating and using classes. The application for the project will be to create a device that responds to taps using the capabilities of the LSM3038 chip.



  1. Create a new Arduino sketch and save it as activity. Once you have done that, copy your LEDBank_p7.h and LEDBank_p7.cpp files into the directory containing activity.ino. Alternately, you could make a new LEDBank folder in your libraries folder and put the two files there, where they will be accessible from any sketch.
  2. Part 1: Accelerometer/Magnetic Sensor Library

  3. Create a new tab and save it as ACSensor_p7.h. Create a second tab and save it as ACSensor_p7.cpp. These two files will be the include file and code file for a class for dealing with the accelerometer/magnetic sensor.

    The Adafruit help pages for the LSM303 sensor.

  4. In your ACSensor_p7.h include file, #define the value ACS_MEMORY_CAP to have the value 20.
  5. Also in your include file, begin a class ACSensor. In the private section, the class should have a field of type Adafruit_LSM303_Accel_Unified, a field of type Adafruit_LSM303_Mag_Unified, an array of type float of length 3 to hold the current accelerometer reading, an array of type float of length 2 to hold the current magnetic sensor reading, and an array of type float of length 3 to hold the gravity estimate. In addition, add a field prior that is an array of type float that is a 2 dimensional array with the first dimension being length ACS_MEMORY_CAP and the second dimension being length 3. Finish with an int field next, which will alway refer to the next open space in the array. You can use the following template.

     * Name and header here
    #ifndef ACSENSOR_P7_H
    #define ACSENSOR_P7_H
    #include <Arduino.h>
    #include <Adafruit_Sensor.h>
    #include <Adafruit_LSM303_U.h>
    // #define definitions here
    // define a Accel/Magnetic sensor class

  6. In your ACSensor_p7.cpp file, define the following member functions for the ACSensor class. As you create each function, put its prototype into the class definition. Remember, you don't need the ACSensor:: prefix for the function names in your prototypes.

    1. void ACSensor::clearPrior(void) - this function should loop over the elements of the prior array and set all of its values to 0.0, then set the next value to zero.

      When you put the prototype for this member function in your class definition, place it in the private section. Do the same for the init member function. Put the rest of the member function prototypes in the public section of the class.

    2. void ACSensor::init( void ) - this function should initialize the Accel and Mag sensor objects, call their begin functions, and initialize the rest of the fields to zero (you can call this->clearPrior() to zero out the prior array). To initialize the sensors, you can use the following code (using the names of the fields you created in the prior step).

        // initialize the Mag and Accel objects, each with a unique identifier number
        this->mag = Adafruit_LSM303_Mag_Unified(12345);
        this->acc = Adafruit_LSM303_Accel_Unified(54321);
        // startup mag
        if( !this->mag.begin() )
        // startup acc
        if( !this->acc.begin() )

    3. void ACSensor::update( void ) - this function should use the getEvent methods of both the acc and mag objects to get the most recent data, then store the data in current data fields. it should also add the latest data value to the prior array and increment the next index;

        Declare a variable of type sensors_event_t
        Call the getEvent function of your acs acc field
        In three assignments, assign to your current acc reading array the event.acceleration.x (y and z) values
        Call the getEvent function of your acs mag field
        In two assignments, assign to your current mag reading array the event.magnetic.x (and y) values
        Using this->next as the index, assign the current acceleration reading (3 values) to the prior array.
        Increment the next field, using modulo arithmetic: this->next = (this->next + 1) % ACS_MEMORY_CAP

    4. void ACSensor::calibrate( int N ) - this function should collect N sensor readings from the accelerometer, calculate their average, and store the result in the gravity field. You can use this->update() to refresh the current acceleration readings inside the loop.
    5. ACSensor::ACSensor(void) - this is the first constructor (make it your first function). It should call the init member function.
    6. ACSensor::ACSensor(int N) - this is the second constructor. It should call the init member function and then call the calibrate member function, passing in N.
    7. Create accessor functions float acc_x(void), float acc_y(void), float acc_z(void), float mag_x(void), and float mag_y(void). Each one should return its corresponding measurement as a float. Note, since these are simple accessor functions, it's actually good practice to put them into the .h file inside the ACSensor class definition (instead of just prototypes).
    8. Create accessor functions void ACSensor::acc( float arr[]) and void ACSensor::mag( float arr[] ) that copy the acceleration or magnetic values, respectively, into the given array.
    9. If you wish, go ahead and create functions float ACSensor::compass(void) and float ACSensor::gravityCosAngleX(void) that do the same thing as in the last project.

    Part 2: Activity

  7. The next step is to make a member function that uses the prior readings to detect a set of activities. Start by creating an enumerated type in your class definition. Just after the public: statement, put the following.

      enum Activity {None, Shake, Tap, LeftTilt, RightTilt};

    An enumerated type is a variable type that has specific values, the values in the list. We'll use these values to communicate back from our activity member function. The definition of the activity function is as follows.

    ACSensor::Activity ACSensor::activity(void) - this function should first call the update member function, then compute the average squared X value of the acceleration, the average squared Y value of the acceleration, the average Y value of the acceleration, and the maximum squared Z value of the acceleration. In your loop to compute the sums for the averages, loop this->next times, and divide the sums by that value after the loop.

    Next, the function should check for a tap (max Z^2 value > 380), a shake (average X^2 > 8 and the average Y^2 > 8), a left tilt (avgY < -3), or a right tilt (avgY > 3), in that order. If one of the conditions is true, it should call this->clearPrior() and then return the corresponding ACSensor::Activity value (e.g. ACSensor::Tap). If none of the conditions is true, return ACSensor::None. Do not call clearPrior if none of the conditions are true.

  8. Return to your activity.ino file. Use the template below.

      Name and header here
    #include >Arduino.h<
    #include "ACSensor_p7.h"
    #include "LEDBank_p7.h"
    int main() {
      /* variables here */
      // create an LEDBank variable led
      // create an ACSensor variable, calibrating it with 5 readings  
      led.on(500); // flash the lights, because why not
      for(int quit=0;!quit;) {
        // call the activity member function of acs and save the result
        what = acs.activity();
        // switch on the result and display a different LED pattern for each activity
        delay(5); // (200Hz sampling rate)

  9. Take a video of your activity program working and include it in your wiki or handin folder.

Follow-up Questions

  1. What is a class?
  2. How is using a class easier than working with a struct?
  3. If I have a variable ACSensor acs, what is the relationship between acs and the specival variable this inside one of the ACSensor a member functions?
  4. What is the purpose of the private/protected keywords in the class definition?



Each week you will write a brief report about your project. In general, your intended audience for your write-up is your peers not in the class. From week to week you can assume your audience has read your prior reports. Your wiki report should explain to friends what you accomplished in this project and to give them a sense of how you did it.

Your project report should contain the following elements.

  1. Abstract: a brief summary (200 words or less) of the task, in your own words. give the reader context and identify the key purpose(s) of the project. You can assume the reader has read your prior assignments.

    Writing an effective abstract is an important skill. Consider the following questions while writing it.

    • Does it describe the CS concepts of the project (e.g. writing loops and conditionals)?
    • Does it describe the specific project application (e.g. creating applications with the LCD)?
    • Does it describe your the solution or how it was developed (e.g. what code did you write/circuits did you build)?
    • Does it describe the results or outputs (e.g. did your code and circuit work as expected)?
    • Is it concise?
    • Are all of the terms well-defined?
    • Does it read logically and in the proper order?

  2. A description of your solution to the tasks. This should be a description of the form and functionality of your final code and the design of your breadboard circuits. Try to describe your algorithm or code without including actual code in your report. Using 1-2 lines as an example is acceptable. Using simple diagrams or pictures of your board may be helpful when describing your circuits. Note any unique computational solutions or hardware circuits you developed.
  3. A description of any extensions you undertook, including images, videos, or diagrams demonstrating those extensions. If you added any functions, or other design components, note their structure and the algorithms you used.
  4. The answers to any follow-up questions.
  5. A brief description (1-3 sentences) of what you learned.
  6. A list of people you worked with, including TAs, and professors. Include in that list anyone whose code you may have seen, such as those of friends who have taken the course in a previous semester.
  7. Don't forget to label your writeup so that it is easy for others to find. For this lab, use cs153f18project7


Mount the Courses volume. Navigate to the Private sub-directory. Create a new folder called project01. (It's best to avoid spces in the directory name.) Each week, the following items should be submitted here.

Your report should be submitted as a wiki page with the appropriate label, as noted above.