9. Output Devices

Assignment

Group Assignment

Link to this week's group assignment

SSD1315 128x64 OLED Display

We have some OLED display modules lying around the lab, so I decided to use my new capacitve sensing board to do some screen display.

The display module we have uses the SSD1315 display driver with I2C communication protocol.

The SSD1315 display driver is the new version of the widely used SSD1306 driver chip, and it is designed to be compatible with the old SSD1306, so the display driver control code for SSD1306 can be used to drive the SSD1315.

The Adafruit SSD1306 library is a good library to work with the display, the example comes with the library is a good starting point for creating my own program.

During my testing, there was only a small problem. The Adafruit library sets the I2C address of the 128x64 screen to 0x3D, but the display I'm using has an address of 0x3C, which can be found using the i2c_scanner example that comes with the Arduino IDE.

After setting the right address, I can control the screen with no problem, then I just need to upload my program to my own board. I exposed the pins for I2C communication (A4/A5) on my capacitive sensing board, below is a program that displays the current sensor state and the processing time on the OLED.

#include <CapacitiveSensor.h>
#include <Adafruit_SSD1306.h>

const int w = 128, h = 64;
const int addr = 0x3C;
Adafruit_SSD1306 display(w, h);

CapacitiveSensor pads[] = {
  { 2, 3 }, { 2, 4 }, { 2, 5 }, { 2, 6 },
  { 2, 7 }, { 2, 8 }, { 2, 9 }, { 2, 10 },
  { 2, 11 }, { 2, 12 },
};
#define array_len(a) (sizeof(a) / sizeof *(a))

void setup() {
  Serial.begin(9600);
  pinMode(13, OUTPUT);

  if (!display.begin(SSD1306_SWITCHCAPVCC, addr)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;);  // Don't proceed, loop forever
  }
  display.setTextColor(WHITE);
  display.setTextSize(2);
}

void loop() {
  unsigned long start = millis();

  bool touched = false;
  for (int i = 0; i < array_len(pads); ++i) {
    long val = pads[i].capacitiveSensor(30);
    Serial.print(i);
    Serial.print(": ");
    Serial.print(val);
    Serial.print("\t");
    if (val > 200) {
      touched = true;
      break;
    }
  }
  digitalWrite(13, touched);

  unsigned long processing_time = millis() - start;

  Serial.println();
  Serial.println(processing_time);

  display.clearDisplay();
  display.setCursor(0, 0);
  display.println(touched ? "Touched!" : "No touch");
  display.print("T:");
  display.println(processing_time);
  display.display();
}

Buzzer

Since my final project is a custom synthesizer and the capacitive sensing board is suppose to be its input for tones, the plan is to make it generate input for the base frequency of the synth.

The ideal functionality would be that it can output signal that combines multiple fequencies when more than one pad is touched at the same time, but I couldn't figure out how to make it work with the current design only with a ATmega328p.

Still, as a test, I tried to use the built in tone function to generate a single frequency on the remaing output pin (D13) to drive a buzzer to see if the board can at least make a sound.

The following program tries to detect the pad that is last touched when multiple pads are being touched.

#include <CapacitiveSensor.h>

// After adding the code for checking the last touched pad,
// there were not enough memory for the display, so I disabled
// the code for the screen temporarily
//#define SCREEN

#ifdef SCREEN
#include <Adafruit_SSD1306.h>
#define W 128
#define H 64
#define ADDR 0x3C
Adafruit_SSD1306 display(W, H);
#endif

CapacitiveSensor pads[] = {
  { 2, 3 }, { 2, 4 }, { 2, 5 }, { 2, 6 },
  { 2, 7 }, { 2, 8 }, { 2, 9 }, { 2, 10 },
  { 2, 11 }, { 2, 12 },
};
#define array_len(a) (sizeof(a) / sizeof *(a))

void setup() {
  Serial.begin(9600);
  Serial.println();
  pinMode(13, OUTPUT);

#ifdef SCREEN
  if (!display.begin(SSD1306_SWITCHCAPVCC, ADDR)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;);  // Don't proceed, loop forever
  }
  Serial.println(F("Init display"));
  display.setTextColor(WHITE);
  display.setTextSize(2);
#endif
}

unsigned long last_touch_time[array_len(pads)] = {};

void loop() {
  unsigned long start = millis();

  int last_touched = -1;
  for (int i = 0; i < array_len(pads); ++i) {
    long val = pads[i].capacitiveSensor(30);
    Serial.print(i);
    Serial.print(": ");
    Serial.print(val);
    Serial.print("\t");

    bool current = val > 200;
    if (current) {
      if (last_touch_time[i] == 0) {
        last_touch_time[i] = millis();
      }
      if (last_touched < 0 || last_touch_time[last_touched] < last_touch_time[i]) {
        last_touched = i;
      }
    } else {
      last_touch_time[i] = 0;
    }
  }
  Serial.println();

  if (last_touched < 0) {
    noTone(13);
  } else {
    tone(13, 300 + last_touched * 100);
  }

  unsigned long processing_time = millis() - start;

  Serial.print(F("Last touched: "));
  Serial.println(last_touched);
  Serial.println(processing_time);

#ifdef SCREEN
  display.clearDisplay();
  display.setCursor(0, 0);
  if (last_touched < 0) {
    display.println(F("No touch"));
  } else {
    display.print(F("Touched "));
    display.println(last_touched);
  }
  display.print(F("T:"));
  display.println(processing_time);
  display.display();
#endif
}