9. Output Devices
Assignment
-
Group assignment:
- Measure the power consumption of an output device.
- Document your work on the group work page and reflect on your individual page what you learned.
-
Individual assignment:
- Add an output device to a microcontroller board you've designed and program it to do something.
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.
Wiring
The ATmega328p has dedicated pins for I2C communication and I reserved them on the PCB. Just connect the A4/A5 pins which correspond to the SDA/SCL pins for I2C communication and connect the power pins.
Arduino Driver Library
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.
Setting the Correct Address
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 buzzer I'm using is a generic 1206 passive buzzer, which can take voltage
from 2V to 7V, and has a resonant frequency of 4000Hz. It can be driven by the
digital pin on my board and controled by the Arduino tone()
function.
The following program tries to detect the pad that is last touched when multiple pads are being touched, and outputs the coresponding frequency of that pad.
Memory Issue
After I first added the code for keeping track of the last touched pad, the board did nothing after I uploaded the program. After some print debugging, I found that the program has already crashed when initializing the OLED screen at the very begining. It even failed to output the failure message. It started working again after I removed some pad checking code, or removing the capacitive sensor library, turns out that putting all these functionality together consumes too much of the board's memory. It passes the program size check of the Arduino IDE but will fail when initializing the memory for the display buffer of the OLED screen.
So I commented out the code for the OLED screen and ended up inspecting the output from the serial monitor, which is less convient.
#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
}
Source files
- OLED screen program: atmega328-capacitive-screen.ino
- Buzzer program: atmega328-capacitive-tone.ino