V kanceláři občas nadáváme a občas se dokonce potřebujeme pochválit. Proto mě napadlo pro tyto případy vytvořit krabičku, která to udělá za nás a přidá prvek překvapení.
Nazval jsem ji Red-Green Machine a slovy klasika „Princip je jednoduchý…“. Červené tlačítko vybere nějaký nelichotivý výrok a na účel zeleného už přijdete sami.
1. Komponenty
- krabička 100x85x90mm (můžete přizpůsobit velikosti tlačítek a reproduktoru)
- čtečka SD karet
- SD karta
- dvě tlačítka (čím větší, tím lepší) požil jsem arkádová tlačítka s podsvícením
- Arduino UNO
- reproduktor o impedanci 8ohmu
- zesilovač 3W
- odpory 2x 10K, 2x 220ohm
- kablíky na propojení
2. Jak to funguje
Na kartě jsou dva adresáře „red“ a „green“ v každém jsou uloženy zvukové soubory s požadovaným obsahem. Soubory jsou ve formátu wav 16Hz, 8bit mono. Při stisku tlačítka se náhodně vybere jeden ze souborů ve zvoleném adresáři a přehraje se.
Pro zelené tlačítko jsou použity superlativy z Apple KeyNote a pro červené tlačítko „Lakatoš“.
3. Zapojení
Základní zapojení je uvedeno na začátku zdrojového kódu. Podle popisu propojíme jednotlivé komponenty.
Na SD kartu, naformatovanou FAT32, rozbalíme obsah zip souboru RedGreenMachine_pack.
4. Program
//SDCard - Uno - Mega
//GND - GND - GND
//3.3V - 3.3V - 3.3V
//CS - 10 - 53(SS)
//MOSI - 11 - 51
//SCK - 13 - 52
//MISO - 12 - 50
//Repro
//- - GND - GND
//+ - 9 - 5, 6, 11, 46
//Red button
//press - 4 - 4
//light - 5 - 5
//Green button
//press - 2 - 2
//light - 3 - 3
#include <SD.h>
#include <TMRpcm.h>
#include <SPI.h>
#define MIN_VOLUME 1
#define MAX_VOLUME 5
#define BLINK_DELAY 1000//1sec in ms
#define BLINK_DURATION 500//0.5sec in ms
#define TIME_TO_SET_VOLUME 3000//3s in ms
#define SD_ChipSelectPin 10//pin 10 on most Arduino boards, 53 on a mega
#define VERSION 0.07
// 0.07 - Dont create list of files to save memory (filse must be in format x.wav, where x is number from 0 to file count)
// 0.06 - Support for change volume at begin
// 0.05 - Debug under preprocessor
// 0.04 - Documentatiorn for connection
// 0.03 - Support for lights in buttons
// 0.02 - Support for buttons
// 0.01 - TMRpcm test
//#define DEBUG 1//with debug on no sfx while play
//variables declaration
TMRpcm tmrpcm;//https://github.com/TMRh20/TMRpcm
int redFileCount;
int greenFileCount;
int sfxVolume = 1;
bool initialized = false;
String FILE_EXTENSION = ".WAV";
const int greenButtonPin = 2;
const int greenLedPin = 3;
const int redButtonPin = 4;
const int redLedPin = 5;
int buttonState = 0;
uint32_t lastBlink;
uint32_t endTimeToSetVolume;
bool redButtonPressed = false;
bool greenButtonPressed = false;
void setup()
{
Serial.begin(115200);
#ifdef DEBUG
Serial.print("RedGreen machine starting in version:");
Serial.println(VERSION);
#endif //DEBUG
randomSeed(analogRead(0));//initialize random generator from analog input
//set pin mode
pinMode(SD_ChipSelectPin, OUTPUT);
pinMode(redLedPin, OUTPUT);
pinMode(greenLedPin, OUTPUT);
pinMode(redButtonPin, INPUT);
pinMode(greenButtonPin, INPUT);
tmrpcm.speakerPin = 9;//set to 5,6,11 or 46 for Mega, 9 for Uno, Nano, etc.
#ifdef DEBUG
Serial.println("Initializing SD Card");
#endif //DEBUG
if (!SD.begin(SD_ChipSelectPin))
{
#ifdef DEBUG
Serial.println("SD cart init fail!");
#endif //DEBUG
return;
}
#ifdef DEBUG
Serial.println("Reading files from SD Card");
#endif //DEBUG
File directory = SD.open("/red");
redFileCount = getFileCount(directory, FILE_EXTENSION);
directory.close();
directory = SD.open("/green");
greenFileCount = getFileCount(directory, FILE_EXTENSION);
directory.close();
#ifdef DEBUG
Serial.print("Redn files:");
Serial.println(redFileCount);
Serial.print("Green files:");
Serial.println(greenFileCount);
#endif //DEBUG
#ifdef DEBUG
Serial.print("Set default volume:");
Serial.println(sfxVolume);
#endif //DEBUG
tmrpcm.setVolume(1);
tmrpcm.quality(1);
endTimeToSetVolume = millis() + TIME_TO_SET_VOLUME;//set end time for set volume
#ifdef DEBUG
Serial.println("Basic setup done");
#endif //DEBUG
}
void loop()
{
if (initialized)
{
ProcessRedGreenMachine();
}
else
{
uint32_t nowTime = millis();
if (nowTime < endTimeToSetVolume)
{
//allow set volume
ProcessSetVolume();
}
else
{
initialized = true;//ready for machine
}
}
}
//Allow to set volume
//red button - volume down
//green button - volume up
void ProcessSetVolume()
{
BlinkWithLights();
//read green button state
buttonState = digitalRead(greenButtonPin);
if (buttonState == HIGH)
{
if (!greenButtonPressed)
{
greenButtonPressed = true;
//volume up
sfxVolume++;
//check volume maximum
if (sfxVolume > MAX_VOLUME)
{
sfxVolume = MAX_VOLUME;//set max
}
//set volume to library
tmrpcm.setVolume(sfxVolume);
playRandomFile("/green", greenFileCount);//play file to check volume
//extend time for set volume
endTimeToSetVolume = millis() + TIME_TO_SET_VOLUME;//set end time for set volume
#ifdef DEBUG
Serial.print("Set volume up:");
Serial.println(sfxVolume);
#endif //DEBUG
}
}
else
{
greenButtonPressed = false;
}
//read red button state
buttonState = digitalRead(redButtonPin);
if (buttonState == HIGH)
{
if (!redButtonPressed)
{
redButtonPressed = true;
//volume downp
sfxVolume--;
//check volume maximum
if (sfxVolume < MIN_VOLUME)
{
sfxVolume = MIN_VOLUME;//set min volume
}
//set volume to library
tmrpcm.setVolume(sfxVolume);
playRandomFile("/red", redFileCount);//play random file to check volume
//extend time for set volume
endTimeToSetVolume = millis() + TIME_TO_SET_VOLUME;//set end time for set volume
#ifdef DEBUG
Serial.print("Set volume down:");
Serial.println(sfxVolume);
#endif //DEBUG
}
}
else
{
redButtonPressed = false;
}
}
//Read red and green button and according to states play random sfx.
void ProcessRedGreenMachine()
{
buttonState = digitalRead(greenButtonPin);
// check if the pushbutton is pressed.
// if it is, the buttonState is HIGH:
if (buttonState == HIGH)
{
// turn green LED on:
digitalWrite(greenLedPin, HIGH);
if (!greenButtonPressed)
{
greenButtonPressed = true;
playRandomFile("/green", greenFileCount);
#ifdef DEBUG
Serial.println("Green button pressed");
#endif //DEBUG
}
}
else
{
// turn LED off:
digitalWrite(greenLedPin, LOW);
greenButtonPressed = false;
}
buttonState = digitalRead(redButtonPin);
if (buttonState == HIGH)
{
// turn red LED on:
digitalWrite(redLedPin, HIGH);
if (!redButtonPressed)
{
redButtonPressed = true;
playRandomFile("/red", redFileCount);
#ifdef DEBUG
Serial.println("Red button pressed");
#endif //DEBUG
}
}
else
{
// turn LED off:
digitalWrite(redLedPin, LOW);
redButtonPressed = false;
}
}
//play random files in format x.wav
//where x is random number from files count
void playRandomFile(String directoryName, int fileCount)
{
int randomFileIndex = random(fileCount);
String fullFileName = directoryName + "/" + randomFileIndex + FILE_EXTENSION;
#ifdef DEBUG
Serial.print("Selected random file:");
Serial.println(fullFileName);
#endif //DEBUG
tmrpcm.play(fullFileName.c_str());
}
//Return number of files of specific extension in selected dictionary
int getFileCount(File directory, String extension)
{
int fileCount = 0;
while (true)
{
File entry = directory.openNextFile();
if (!entry)
{
// no more files
break;
}
//is file
if (!entry.isDirectory())
{
String fileName = String(entry.name());
//is file with expected extension
if (fileName.endsWith(extension))
{
fileCount++;
}
}
entry.close();
}
directory.rewindDirectory();//return at first file in directory
return fileCount;
}
//Blink with lights to show possibility to change volume.
void BlinkWithLights()
{
uint32_t nowTime = millis();
//time to blink with lights
if (nowTime > lastBlink)
{
if (nowTime < lastBlink + BLINK_DURATION)
{
digitalWrite(greenLedPin, HIGH);
digitalWrite(redLedPin, HIGH);
}
else
{
lastBlink = nowTime + BLINK_DELAY;
digitalWrite(greenLedPin, LOW);
digitalWrite(redLedPin, LOW);
}
}
}
Obvod si zapojíme na nepájivém poli. Ještě jednou zkontrolujeme a připojíme Arduino k počítači. Otevřeme vývojové prostředí s programem a nahrajeme program do Arduina.
6. Krabička
Základem je krabička o rozměrech 100x85x90mm, kterou jsem si vyrobil z bukové 5mm překližky. Po zkušenostech s výrobou bych příště raději použil nějakou plastovou. Jen upozorňuji, že krabička musí mít určitou pevnost, aby odolala návalu vzteku uživatelů.
Do krabičky jsou ze zhora vyvrtány dvě díry o průměru 25mm pro nasazení arkádových tlačítek.

Z boku je vyříznuta díra pro nasazení reproduktoru o výkonu 10W a impedanci 8ohm.

Z druhého boku je vyříznut otvor pro vkládaní SD karty.

6. Kompletace
6.1 Tlačítka
Podle schématu zapojení si na vyvodu LED osvětlení vytvoříme společnou zem (GND), kterou pomocí odporu 10k spojíme s kontaktem tlačítka.

Na druhoý vývod LED kontaktu připájíme odpor 220Ohm a za něj vývod, který připojíme k Arduinu (v obrázku vodič směřujicí nahoru).

K druhému vývodu tlačítka připájíme vodič, který připojíme k Arduinu.

Na červeném tlačíku uděláme připojení stejně, jen GND si spojíme s GND od zeleného tlačítka. Na obrázku je ještě spojení s reproduktorem, které bylo později nahrazeno připojením k vlastnímu zesilovači.

6.2 Zesilovač
Nejprve jsem zkoušel reproduktor připojit na výkonový tranzistor, později na zesilovač, ale v obou připadech to nefungovalo moc dobře. Takže jsem nakonec použil zkompletovaný integrovany obvod s výkonem 3W.

Podle schématu připojíme zesilovač do obvodu.
6.3 SD karta
SD kartu jsem do krabičky vlepil pomocí tavné pistole a připojil k Arduino. Připojení je dobré udělat ještě před montáží tlačítek, protože pak je v krabičce už málo místa pro manipulaci.

6.4 Všechno do krabičky
Jednotlivé součásti máme připojené, takže přichází nejtěžší úkol, dostat to vše do krabičky.
Jednotlivá tlačítka nacvakneme do jejich pouzder, která máme již připevněná v krabičce. Následně nasadíme reproduktor a přišroubujeme ho.

Arduino Uno připevníme na víčko krabičky pomocí tavné pistole.

Následně krabičku zaklopíme a víčko připevníme. Použil jsem univerzální izolepu :).


