System Breakdown

Block Diagram 

At the core of each functional block is a piece of hardware. One step above that is the additional hardware needed to control or interact with the core hardware element. The highest step away from the core is how we interface with it via software. It's how we would use the hardware via code. The blocks themselves are just a portion of how our system works. We always have arrows to indicate the flow of data between these blocks. Let's start at the core of each block and work our way out to the software.

Blocks show a part of the full functionality of the kit. The other part being the arrows the show the flow of data between the blocks.

Block Breakdown 

We'll start looking at just those first two layers of our blocks. The pure hardware component layer and the driver or controller layer. Along side the block diagram from earlier is the schematic of the PCB. Then we'll see how software can interface with the driver or controller sub-block.

Motor

The motor block is composed of the vibration motor and then the support circuitry to drive it. The motor draws too much current to be able to drive it directly from the Arduino. Instead, we are using a MOSFET to act like a switch. If we replace the MOSFET with just a wire (short circuit), the motor would constantly be on as it would be connected right from 5V, through the motor, to ground. The MOSFET is placed between the motor and its connection to ground. When the MOSFET is LOW the gate shuts the flow of electricity to ground and turns off the motor. When set HIGH, electricity is allowed to pass through the MOSFET and then to ground, allowing the motor to run.

The motor is a pretty simple thing to drive. We set a pin output which in turn sets the MOSFET to drive the motor. LOW being off and HIGH being on. In the sketch below, the pin which the motor driver circuit will use is defined. Then it is turned on for 100 milliseconds and then off for 1000 milliseconds.

Download file Copy to clipboard
1
2
3
4
5
6
7
8
9
10
11
12
13
#define MOTOR_PIN 5

void setup() {
  pinMode(MOTOR_PIN, OUTPUT);
  digitalWrite(MOTOR_PIN,LOW);
}

void loop() {
  digitalWrite(MOTOR_PIN,HIGH);
  delay(100);
  digitalWrite(MOTOR_PIN,LOW):
  delay(1000);
}

Buttons

The calibration button and microswitch are the same type of component. They only complete a circuit when the button or switch is pressed. Even though they are simple they still need some support circuitry. In this case a network of pull-up resistors. We use pull up resistors because they give two distinct state, GND or VCC. Without them the circuit would be GND > Button > pin. And while this would run it won't give you distinct states. When the circuit was closed you would get the logical 0 (GND) but when it was left open you would not be able to be certain it's returning to a logical 1 (VCC). But by using a pull down or up resistor we can guarantee a logical 1 or 0. That's why the calibration button and microswitch each get a 10k resistor. Also by using a resistor, when a connection from VCC to GND is made it isn't a short.

The code to check if the buttons are pressed is below. It just checks if the value of the pin assigned to that button is either HIGH or LOW and based on that, it turns the LED on the Arduino on or off.

Download file Copy to clipboard
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#define BUTTON_PIN 4

void setup() {
  pinMode(BUTTON_PIN, INPUT);
  pinMode(13,OUTPUT);
  digitalWrite(13,LOW);
}

void loop() {
  if(!digitalRead(BUTTON_PIN){
    digitalWrite(13,HIGH);
  } else {
    digitalWrite(13,LOW);
  }
}

OLED screen

If you flip over the OLED module you can see a lot of pins coming out of the screen yet we only use 7 pins to interface with it. That is because also on the back of the OLED screen is the SH1106 IC with acts as an intermediary between the screen and the Arduino. We give the SH1106 commands and it deals with writing the pixels to the screen. For more information on the SH1106 check out this datasheet. To make coding the OLED screen even easier we use a library. By using the U8glib library, we can set the pins we are using once and just use a name like screen to make references to the OLED screen in our code. Below is an example of how we do just that. We make an object that holds our screen values called 'screen'. Then in 'void draw(void)' we tell it what to draw. And when we tell it to draw that in the main loop, instructions are sent via the pins defined in the U8glib library to the SH1106 which then drawn the characters to the OLED screen.

Download file Copy to clipboard
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

#include "U8glib.h"

U8GLIB_SH1106_128X64 screen(13, 11, 10, 9, 12);	// SW SPI Com: SCK = 13, MOSI = 11, CS = 10, A0 = 9

void draw(void) {  
  screen.setFont(u8g_font_unifont);
  screen.drawStr( 0, 22, "Hello World!");
}

void setup(void) {
}

void loop(void) {
  // picture loop
  screen.firstPage();  
  do {
    draw();
  } while( screen.nextPage() );

  // rebuild the picture after some delay
  //delay(50);
}

IMU

The black square on the IMU pcb has a lot of functionality. It houses the a 3-axis gyroscope and a 3-axis accelerometer. But what's special is it also contains a digital motion processor (DMP). The DMP takes all the data coming from each sensor and uses a process called sensor fusion to make the results more accurate. The DMP also can send us those results in many different formats. To use the DMP we'll need a library called, MPU6050 library. Using this library we can create an object which the can be referenced and called on in the Arduino IDE. In the example we open communication with the DMP on the IMU and ask for some simple gyro and accelerometer data, then write that to the serial monitor.

Download file Copy to clipboard
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

#include "I2Cdev.h"
#include "MPU6050.h"

MPU6050 accelgyro;

int16_t ax, ay, az;
int16_t gx, gy, gz;

void setup() {
    Serial.begin(38400);

    // initialize device
    Serial.println("Initializing I2C devices...");
    accelgyro.initialize();
}

void loop() {
    // read raw accel/gyro measurements from device
    accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);

    #ifdef OUTPUT_READABLE_ACCELGYRO
        // display tab-separated accel/gyro x/y/z values
        Serial.print("a/g:\t");
        Serial.print(ax); Serial.print("\t");
        Serial.print(ay); Serial.print("\t");
        Serial.print(az); Serial.print("\t");
        Serial.print(gx); Serial.print("\t");
        Serial.print(gy); Serial.print("\t");
        Serial.println(gz);
    #endif
}