//https://wiki.seeedstudio.com/Grove-16x2_LCD_Series/
//https://wiki.seeedstudio.com/Grove-12_Key_Capacitive_I2C_Touch_Sensor_V2-MPR121/
//https://github.com/thomasfredericks/Metro-Arduino-Wiring
//https://github.com/daPhoosa/MedianFilter
#include <Metro.h>
#include <Adafruit_NeoPixel.h>
#include <Adafruit_MPR121.h>
#include <rgb_lcd.h>
#include <Wire.h>
#include <EEPROM.h>
#include <MedianFilter.h>
#define BUZZER 5
#define PIN 6
#define MIC A0
#define MIC_TRESHOLD 300
#define LEVEL_COEF 3
#define INTEGRATION_COEF 0.02
#define APPLAUSE_START_TRESHOLD 100
#define TIME_MIN 3
#define TIME_MAX 60
#define BP_START 0
#define BP_MODE 1
#define BP_MINUS 2
#define BP_PLUS 3
// How many NeoPixels are attached to the Arduino?
#define NUMPIXELS 60
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
#define DELAYVAL 500
Adafruit_MPR121 cap = Adafruit_MPR121();
MedianFilter Filter(50, 0);
const int sampleWindow = 20; // Sample window width in mS (50 mS = 20Hz)
unsigned int sample;
unsigned int ApplausePeak = 0; // peak-to-peak level
float ApplauseLevel = 0;
float ApplauseScore = 0;
String FirstLine="";
int Mode;
int Timing;
int HourGlassTotalSecondes;
int HourGlass;
int Minute;
bool PointSeconde;
bool Touch[12];
Metro Tick = Metro(100);
Metro Tick1s = Metro(1000);
enum Modes {HOURGLASS, APPLAUSEMETER, AUTO};
enum States{INIT, SETUP, TIMER, APPMETER, APP_BEGIN, APP_END};
struct _FiniteStateMachine
{
int State;
int PreviousState;
long Cycles;
bool Change;
} FSM;
rgb_lcd lcd;
int colorR = 100;
int colorG = 100;
int colorB = 100;
//***********************************************************
void setup()
{
//analogReference(INTERNAL);
pixels.begin(); // INITIALIZE NeoPixel pixels object (REQUIRED)
pixels.clear();
pixels.show();
Serial.begin(9600);
EEPROM.get(0, Mode);
if(Mode > AUTO)
{
Mode=AUTO;
EEPROM.put(0, Mode);
}
if(Mode < HOURGLASS)
{
Mode=HOURGLASS;
EEPROM.put(0, Mode);
}
EEPROM.get(2, Timing);
if(Timing > TIME_MAX)
{
Timing=TIME_MAX;
EEPROM.put(2, Timing);
}
if(Timing < TIME_MIN)
{
Timing=TIME_MIN;
EEPROM.put(2, Timing);
}
FSM_init();
lcd.begin(16, 2);
lcd.clear();
lcd.setRGB(colorR, colorG, colorB);
if (!cap.begin(0x5B))
{
Serial.println("MPR121 not found, check wiring?");
while (1);
}
}
//***********************************************************
void loop()
{
TouchScan();
FSM_compute();
}
//***********************************************************
void TouchScan()
{
// Keeps track of the last pins touched
// so we know when buttons are 'released'
static uint16_t lasttouched = 0;
static uint16_t currtouched = 0;
// Get the currently touched pads
currtouched = cap.touched();
for (uint8_t i=0; i<12; i++)
{
if ((currtouched & _BV(i)) && !(lasttouched & _BV(i)) )
{
Serial.print(i); Serial.println(" touched");
Touch[i]=true;
}
else Touch[i]=false;
}
lasttouched = currtouched;
}
//***********************************************************
void FSM_init()
{
FSM.State = INIT;
FSM.PreviousState = -1;
FSM.Cycles = 0;
}
//***********************************************************
int FSM_compute()
{
if (FSM.Cycles < 4000000000) FSM.Cycles++;
if (FSM.State != FSM.PreviousState) FSM.Change = true;
else FSM.Change = false;
FSM.PreviousState = FSM.State;
if (FSM.Change)
{
FSM.Cycles = 0;
}
switch (FSM.State)
{
case INIT: //---------------------------------------------------
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Applause meter");
lcd.setCursor(0, 1);
lcd.print("by l'Ecole LDLC");
pixels.clear();
pixels.show();
for (int i = 0; i < NUMPIXELS; i++) // actaulise the strip with the colors
{
pixels.setPixelColor(i, pixels.Color(0, 0, 100));
pixels.show();
tone(BUZZER, 60, 10);
delay(20);
}
pixels.show();
pixels.clear(); // Set all pixel colors to 'off'
pixels.show();
FSM.State=SETUP;
break;
case SETUP: //---------------------------------------------------
MicRecord(false);
if (FSM.Change)
{
AfficheMode();
AfficheTiming();
tone(BUZZER, 880);
delay(200);
tone(BUZZER, 440, 800);
delay(1000);
ApplauseLevel = 0;
ApplauseScore = 0;
}
if(Touch[BP_MODE])
{
Mode++;
if(Mode > AUTO) Mode=HOURGLASS;
AfficheMode();
AfficheTiming();
EEPROM.put(0, Mode);
tone(BUZZER, 440, 50);
}
if(Mode != APPLAUSEMETER)
{
if(Touch[BP_PLUS])
{
Timing++;
if(Timing > TIME_MAX) Timing=TIME_MAX;
AfficheTiming();
EEPROM.put(2, Timing);
tone(BUZZER, 440, 50);
}
if(Touch[BP_MINUS])
{
Timing--;
if(Timing < TIME_MIN) Timing=TIME_MIN;
AfficheTiming();
EEPROM.put(2, Timing);
tone(BUZZER, 440, 50);
}
}
if(Touch[BP_START])
{
switch(Mode) //{HOURGLASS, APPLAUSEMETER, AUTO};
{
case HOURGLASS:
case AUTO:
FSM.State= TIMER;
break;
case APPLAUSEMETER:
FSM.State= APPMETER;
break;
}
}
break;
case TIMER: //---------------------------------------------------
MicRecord(false);
if (FSM.Change)
{
tone(BUZZER, 440);
delay(200);
tone(BUZZER, 880, 500);
delay(500);
HourGlass=Timing;
Minute=0;
AfficheTime();
while(Tick1s.check() == 1) {};
}
if ((Tick1s.check() == 1) )
{
if(PointSeconde) PointSeconde=false;
else PointSeconde=true;
Minute--;
if(Minute < 0)
{
Minute=59;
if(HourGlass) HourGlass--;
}
tone(BUZZER, 60, 20);
AfficheTime();
ledpixels();
}
if((HourGlass== 0)&&(Minute==0))
{
switch(Mode)
{
case HOURGLASS:
FSM.State=INIT;
break;
case AUTO:
FSM.State= APPMETER;
break;
}
}
if(Touch[BP_START])
{
FSM.State=INIT;
}
break;
case APPMETER: //---------------------------------------------------
MicRecord(true);
if (FSM.Change)
{
tone(BUZZER, 440);
delay(200);
tone(BUZZER, 880, 500);
delay(500);
}
ledpixels();
AfficheScore(false);
if(ApplauseLevel > APPLAUSE_START_TRESHOLD) FSM.State=APP_BEGIN;
if(Touch[BP_START])
{
FSM.State=INIT;
}
break;
case APP_BEGIN: //---------------------------------------------------
MicRecord(true);
ledpixels();
AfficheScore(false);
if(ApplauseLevel < APPLAUSE_START_TRESHOLD)
{
if (FSM.Cycles >200) FSM.State=APP_END;
}
else FSM.Cycles =0;
if(Touch[BP_START])
{
FSM.State=APP_END;
}
break;
case APP_END: //---------------------------------------------------
MicRecord(false);
if (FSM.Change)
{
tone(BUZZER, 880);
delay(200);
tone(BUZZER, 440, 800);
delay(500);
ledpixels();
AfficheScore(true);
}
if(Touch[BP_START])
{
FSM.State=INIT;
}
break;
}
return (FSM.State);
}
//***********************************************************
void AfficheMode()
{
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(0, 0);
lcd.print("Mode: ");
switch(Mode)
{
case HOURGLASS:
lcd.print("Hourglass");
break;
case APPLAUSEMETER:
lcd.print("App.meter");
break;
case AUTO:
lcd.print("Auto");
break;
}
}
void AfficheTiming()
{
lcd.setCursor(0, 1);
lcd.print(" ");
if(Mode != APPLAUSEMETER)
{
lcd.setCursor(0, 1);
lcd.print("Time: ");
lcd.print(Timing);
lcd.print(" minutes");
}
}
//***********************************************************
void AfficheScore(bool Ended)
{
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 1);
if(!Ended)lcd.print("App.Score= ");
else lcd.print("Final Score=");
lcd.print((int)ApplauseScore);
}
//***********************************************************
void AfficheTime()
{
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print("HourGlass= ");
//lcd.print(HourGlassTotalSecondes);
if(HourGlass<10) lcd.print(' ');
lcd.print(HourGlass);
lcd.print(":");
if(Minute<10) lcd.print(0);
lcd.print(Minute);
}
//**********************************************************************************
void MicRecord(bool On)
{
unsigned long startMillis = millis(); // Start of sample window
unsigned int signalMax = 0;
unsigned int signalMin = 1024;
while (millis() - startMillis < sampleWindow)
{
sample = analogRead(MIC);
if (sample < 1024) // toss out spurious readings
{
if (sample > signalMax)
{
signalMax = sample; // save just the max levels
}
else if (sample < signalMin)
{
signalMin = sample; // save just the min levels
}
}
}
if (On) ApplausePeak = signalMax - signalMin; // max - min = peak-peak amplitude
else ApplausePeak = 0;
if(ApplausePeak > MIC_TRESHOLD) Filter.in(ApplausePeak);
else Filter.in(0);
ApplauseLevel = Filter.getMean() * LEVEL_COEF;
if(ApplauseLevel > 1000) ApplauseLevel=1000;
if ((Tick.check() == 1) )
{
if(ApplausePeak > MIC_TRESHOLD) ApplauseScore+= ApplauseLevel * INTEGRATION_COEF;
}
if(ApplauseScore > 1000) ApplauseScore=1000;
Serial.print(ApplausePeak);
Serial.print(" ");
Serial.print(MIC_TRESHOLD);
Serial.print(" ");
Serial.print(ApplauseLevel);
Serial.print(" ");
Serial.print(ApplauseScore);
Serial.print(" ");
Serial.print(1024);
Serial.print(" ");
Serial.print(0);
Serial.println();
}
//************************************************************************
void ledpixels()
{
int LedLevel, LedScore, LedPeak;
int R[NUMPIXELS]; // red
int G[NUMPIXELS]; // green
int B[NUMPIXELS]; // blue
for(int i=0; i<NUMPIXELS; i++) // reintialise R G and B all cells at 0
{
R[i]=0;
G[i]=0;
B[i]=0;
}
pixels.clear(); // Set all pixel colors to 'off'
switch (FSM.State)
{
case APPMETER:
case APP_BEGIN:
case APP_END:
LedLevel = map(ApplauseLevel, 0, 1000, 5, NUMPIXELS-1); // map the value to the number of pixel on the pixels
LedScore = map(ApplauseScore, 0, 1000, 5, NUMPIXELS-1); // map the max value to the number of pixel on the pixels
LedPeak = map(ApplausePeak, 0, 1023, 5, LedLevel ); // map the peak to peak to the number of led -2
for (int i = 0; i < LedLevel; i++) // print in red the actual value
{
B[i]=10;
}
for (int i = 0; i < LedPeak; i++) // print in blue the clap value
{
R[i]=20;
}
for (int i = 0; i < NUMPIXELS; i++) // actaulise the pixels with the colors
{
pixels.setPixelColor(i, pixels.Color(R[i], G[i], B[i]));
}
if (FSM.State != APP_END)
{
pixels.setPixelColor(LedScore, pixels.Color(255, 0, 0)); // actualise the max value
pixels.setPixelColor(LedScore-1, pixels.Color(255, 0, 0));
pixels.setPixelColor(LedScore-2, pixels.Color(255, 0, 0));
}
else
{
pixels.setPixelColor(LedScore, pixels.Color(0,255, 0)); // actualise the max value
pixels.setPixelColor(LedScore-1, pixels.Color(0,255, 0));
pixels.setPixelColor(LedScore-2, pixels.Color(0,255, 0));
}
break;
case TIMER:
HourGlassTotalSecondes= HourGlass*60 + Minute;
LedLevel = map(HourGlassTotalSecondes, 0, (Timing*60), 5, NUMPIXELS); // map the value to the number of pixel on the pixels
for (int i = 0; i < LedLevel+1; i++) // print in red the actual value
{
if(HourGlassTotalSecondes >= (Timing*60)/2) B[i]=50;
else if (HourGlassTotalSecondes >= (Timing*60)/3) G[i]=50;
else if (HourGlassTotalSecondes >= 60) G[i]=30, R[i]=50;
else R[i]=50;
}
for (int i = 0; i < NUMPIXELS; i++) // actaulise the pixels with the colors
{
pixels.setPixelColor(i, pixels.Color(R[i], G[i], B[i]));
}
if(PointSeconde)
{
if (HourGlassTotalSecondes >= 60)
{
pixels.setPixelColor(LedLevel, pixels.Color(200, 200, 200));
pixels.setPixelColor(NUMPIXELS-1, pixels.Color(200, 200, 200));
}
else
{
pixels.setPixelColor(LedLevel, pixels.Color(255, 0, 0));
pixels.setPixelColor(NUMPIXELS-1, pixels.Color(255, 0,0));
}
}
break;
}
pixels.show(); // refresh all the led pixels
}
Comments