Skip to content

Instantly share code, notes, and snippets.

@tueddy
Created January 3, 2021 13:01
Show Gist options
  • Save tueddy/50ec8868b52118fd1ef2e09f7bab4245 to your computer and use it in GitHub Desktop.
Save tueddy/50ec8868b52118fd1ef2e09f7bab4245 to your computer and use it in GitHub Desktop.
Pietbox
/*
######
# # # ###### ##### ##### #### # #
# # # # # # # # # # #
###### # ##### # ##### # # ##
# # # # # # # # ##
# # # # # # # # # #
# # ###### # ##### #### # #
PietBox
Copyright (c) 2018/2019 Dirk Carstensen. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
// PietBox
#include "Settings.h"
#include "Configuration.h"
#include "Button.h"
#include "carddata.h"
// WIFI
#include <WiFi.h>
#include "SDEditor.h"
#include <ESPmDNS.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include "MyAsyncWiFiManager.h"
#include <DNSServer.h>
#include <esp_wifi.h>
//#include <ArduinoOTA.h>
// SD card
#include <FS.h>
#include <SD.h>
#include <SPI.h>
// rotary encoder
#include <Encoder.h>
// audio
#include <AudioFileSourceSD.h>
#include <AudioFileSourceSPIFFS.h>
#include <AudioFileSourceICYStream.h>
#include <AudioFileSourceID3.h>
#include <AudioFileSourceBuffer.h>
#include <AudioGenerator.h>
#include <AudioGeneratorMP3.h>
#include <AudioGeneratorWAV.h>
#include <AudioGeneratorAAC.h>
#include <AudioOutputI2S.h>
// NFC
#include <MFRC522Extended.h>
#include <PN532_SPI.h>
#include "PN532.h"
//#include "Wiegand.h"
// Neopixel
#include <NeoPixelBus.h>
#include <NeoPixelAnimator.h>
//for LED status
#include <Ticker.h>
// google TTS
#include <google-tts.h>
const char PROGMEM cardsFileName[] = "/cards.json";
// classes
MFRC522Extended rfid_522(PIN_RFID_SS, PIN_RFID_RST);
PN532_SPI pn532spi(SPI, PIN_RFID_SS);
PN532 rfid_532(pn532spi);
AsyncWebServer server(80);
DNSServer dns;
AsyncWebSocket ws("/ws");
AsyncEventSource events("/events");
SPIClass SPI2;
Encoder myEnc(PIN_ENCODER_1, PIN_ENCODER_2);
Button encoderButton(PIN_ENCODER_BUTTON, LOW);
// NeoPixel
const uint8_t AnimationChannels = 1;
unsigned long startTimeNeoPixel;
NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> pixels(NEOPIXEL_NUM + 1, PIN_NEOPIXEL_DIN);
NeoPixelAnimator animations(AnimationChannels);
#define colorSaturation 128
RgbColor red(colorSaturation, 0, 0);
RgbColor green(0, colorSaturation, 0);
RgbColor blue(0, 0, colorSaturation);
RgbColor white(colorSaturation);
RgbColor black(0);
// what is stored for state is specific to the need, in this case, the colors.
// basically what ever you need inside the animation update function
struct MyAnimationState
{
RgbColor StartingColor;
RgbColor EndingColor;
};
// one entry per pixel to match the animation timing manager
MyAnimationState animationState[AnimationChannels];
Ticker ticker;
// variables (RTC Memory)
RTC_DATA_ATTR uint32_t sleepCount;
static bool hasSD = false;
static bool hasExternalPower = false;
RTC_DATA_ATTR rfidfReaderType readerType = rtUnknown;
boolean card_present = false;
static bool shutdownFlag = false;
static bool togggleState = false;
static float maxVolume = 0.25;
static bool WiFiStarted = false;
String uid = "";
uint32_t lastTimeButtonPressed;
AudioFileSource *audio_in;
AudioFileSourceBuffer *audio_buff;
AudioFileSourceID3 *audio_id3;
AudioGenerator *audio_generator;
AudioOutputI2S *audio_out;
String currentTitle;
String currentPerformer;
String currentAlbum;
// simple blend function
void BlendAnimUpdate(const AnimationParam& param)
{
// this gets called for each animation on every time step
// progress will start at 0.0 and end at 1.0
// we use the blend function on the RgbColor to mix
// color based on the progress given to us in the animation
RgbColor updatedColor = RgbColor::LinearBlend(
animationState[param.index].StartingColor,
animationState[param.index].EndingColor,
param.progress);
// show current volume on neopixel ring
uint32_t neopixelValue = map(config.currentVolume, 0, stepsPerTurn, 0, (NEOPIXEL_NUM) * 255);
uint8_t fullLEDs = (neopixelValue / 255);
uint8_t lastLED = (neopixelValue % 255);
uint8_t i;
// apply the color to the strip
for(i=0; i < (fullLEDs); i++) {
pixels.SetPixelColor(i + 1, updatedColor);
}
// set last LED
if (lastLED != 0) {
pixels.SetPixelColor(fullLEDs + 1, updatedColor);
}
}
/*
* download text to speech file (in a separate task)
*/
void ttsDownloadTaskFunc( void * parameter )
{
while ((!shutdownFlag) && (WiFi.status() != WL_CONNECTED)) { // +++ timeout +++
delay(250);
}
String url;
TTS tts;
String text = "Petebox spielt " + currentTitle + " von " + currentPerformer;
Serial.println("download text-to-speech file for: " + text + '"');
url = tts.getSpeechUrl(text, "de");
Serial.println("text to speech url: '" + url + "'");
Serial.println("text-to-speech task finished");
Serial.flush();
vTaskDelete( NULL );
}
/*
* audio status callback: Called when there's a warning or error (like a buffer underflow or decode hiccup
*/
void audioStatusCallback(void *cbData, int code, const char *string)
{
const char *ptr = reinterpret_cast<const char *>(cbData);
// Note that the string may be in PROGMEM, so copy it to RAM for printf
char s1[64];
strncpy_P(s1, string, sizeof(s1));
s1[sizeof(s1)-1]=0;
Serial.printf("AUDIO STATUS(%s) '%d' = '%s'\n", ptr, code, s1);
Serial.flush();
}
/*
* metadata callback for ID3 tag, an ICY block, etc.
* ID3 callback for: Title = 'Der kleine Drache Kokosnuss - 01'
* ID3 callback for: Performer = 'Ingo Siegner'
* ID3 callback for: Album = '01/Der kleine Drache Kokosnuss'
* ID3 callback for: Year = '2010'
*/
void audioMetadataCallback(void *cbData, const char *type, bool isUnicode, const char *string)
{
(void)cbData;
String s;
Serial.printf("ID3 callback for: %s = '", type);
if (isUnicode) {
string += 2;
}
while (*string) {
char a = *(string++);
if (isUnicode) {
string++;
}
s = s + a;
Serial.printf("%c", a);
}
Serial.printf("'\n");
Serial.flush();
// get interesting metadata
if (strcmp(type, "Title") == 0) {
currentTitle = s;
currentTitle.trim();
}
if (strcmp(type, "Performer") == 0) {
currentPerformer = s;
currentPerformer.trim();
}
if (strcmp(type, "Album") == 0) {
currentAlbum = s;
currentAlbum.trim();
}
}
/*
* get the audio source
*/
audioSource getAudiofileSource(String filename) {
String myFilename = filename;
filename.toLowerCase();
if (myFilename.startsWith("http")) {
return asICY;
} else
if (myFilename.startsWith("/")) {
return asSDCard;
} else
return asUnsupported;
}
/*
* get the audio format
*/
audioFormat getAudiofileFormat(String filename) {
String myFilename = filename;
myFilename.toLowerCase();
myFilename.trim();
if (myFilename.endsWith(".mp3")) {
return afMP3;
} else
if (myFilename.endsWith(".wav")) {
return afWAV;
} else
if (myFilename.endsWith(".aac")) {
return afAAC;
} else
return afUnsupported;
}
/*
* check is valid audio
*/
bool isAudiofile(String filename) {
String myFilename = filename;
filename.toLowerCase();
filename.trim();
if ((myFilename.endsWith(".mp3")) || (myFilename.endsWith(".wav")) || (myFilename.endsWith(".aac"))) {
return true;
} else {
return false;
}
}
/*
* read one line from file
*/
String readLine(File file) {
String received = "";
char ch;
while(file.available()){
ch = file.read();
if(ch == '\n') {
return received;
} else {
received+=ch;
}
}
return received;
}
/*
* stop playing audio
*/
void stopPlaying(){
// close currently playing file first
if (audio_generator && (audio_generator->isRunning())){
Serial.println("stop playing " + config.currentPlaylist + " (" + String(config.currentFileIndex) + ")");
audio_generator->stop();
// wait for playing has stopped
while (audio_generator->isRunning()){
delay(50);
}
audio_in->close();
delete(audio_generator);
audio_generator = NULL;
Serial.println("audio closed");
}
currentTitle = "";
currentPerformer = "";
currentAlbum = "";
// shutdown amplifier
Serial.println("shutdown amplifier/SD-card..");
digitalWrite(PIN_DAC_SD, LOW);
if (!WiFiStarted) {
// turn of neopixel LED
Serial.println("turn of neopixel LED..");
uint8_t i;
for(i=0; i <= (NEOPIXEL_NUM); i++) {
pixels.SetPixelColor(i, black);
}
pixels.Show();
}
}
/*
* get a file from dir with index
*/
String getFileFromDir(File dir, int currentFileIndex){
if (!dir) {
return "";
}
dir.rewindDirectory();
int idx = 0;
int fileFound = false;
String FileToPlay;
while (!fileFound) {
File entry = dir.openNextFile();
if (! entry) {
return "";
}
FileToPlay = entry.name();
if (isAudiofile(FileToPlay)) {
Serial.println("search getFileFromDir, index: " + String(idx) + ": '" + FileToPlay + "'"); //Printing for debugging purpose
if (idx == currentFileIndex) {
fileFound = true;
return FileToPlay;
}
idx = idx + 1;
}
entry.close();
}
}
/*
* play audio file
*/
bool playFile(String currentPlaylist, int currentFileIndex, long filepos){
// wakeup amplifier/sd card
Serial.println("wakeup the amplifier/SD-card..");
pinMode(PIN_DAC_SD, OUTPUT);
digitalWrite(PIN_DAC_SD, HIGH);
delay(2);
if (!SD.exists(currentPlaylist)) {
Serial.print("Playlist file not found: "); Serial.println(currentPlaylist);
digitalWrite(PIN_DAC_SD, LOW);
return false;
}
// open the playlist
File myFile = SD.open(currentPlaylist);
if (!myFile) {
Serial.print("Cannot open the playlist file: "); Serial.println(currentPlaylist);
return false;
}
// get the file to play from m3u playlist
String FileToPlay;
int idx = 0;
int fileFound = false;
while (myFile.available() && (!fileFound)) {
FileToPlay = readLine(myFile);
Serial.println("index: " + String(idx) + ": '" + FileToPlay + "'"); //Printing for debugging purpose
// do some action here
if (idx == currentFileIndex) {
fileFound = true;
break;
}
idx = idx + 1;
}
// close the file properly
myFile.close();
// index out of bounds?
if (!fileFound) {
Serial.print("index: " + String(idx) + " not found in playlist file: "); Serial.println(currentPlaylist);
return false;
}
// is the audio source supported?
audioSource currentAudioSource = getAudiofileSource(FileToPlay);
if (currentAudioSource == asUnsupported) {
Serial.println("Audio source not supported: '" + FileToPlay + "'");
// turn on red LED
pixels.SetPixelColor(0, red);
pixels.Show();
return false;
}
// is the audio type supported?
audioFormat currentAudioFormat = getAudiofileFormat(FileToPlay);
if (currentAudioFormat == afUnsupported) {
Serial.println("Audio format not supported: '" + FileToPlay + "'");
// turn on red LED
pixels.SetPixelColor(0, red);
pixels.Show();
return false;
}
// source is web URL?
if (currentAudioSource == asICY) {
Serial.println("Play URL from playlist: '" + FileToPlay + "', Index(" + String(currentFileIndex) + ")");
Serial.println("start WIFI for streaming..");
startWIFI();
}
else
{
// check file is a directory
File myDir = SD.open(FileToPlay);
if (!myDir) {
File myDir = SD.open(FileToPlay);
}
if (myDir) {
Serial.println("Play from playlist: '" + FileToPlay + "', Index(" + String(currentFileIndex) + ")");
if (myDir.isDirectory()) {
// play files from directory
FileToPlay = getFileFromDir(myDir, currentFileIndex);
fileFound = SD.exists(FileToPlay);
Serial.println("Play directory from playlist: '" + FileToPlay + "', Index(" + String(currentFileIndex) + ")");
}
else
{
// play single file
fileFound = SD.exists(FileToPlay);
}
myDir.close();
}
else {
Serial.println("Play File from playlist: '" + FileToPlay + "', Index(" + String(currentFileIndex) + ")");
fileFound = SD.exists(FileToPlay);
}
// for SD/SPIFFS check file exists
if (!fileFound) {
Serial.println("File not found: '" + FileToPlay + "'");
// turn on red LED
pixels.SetPixelColor(0, red);
pixels.Show();
return false;
}
}
// create audio source
if (currentAudioSource == asSDCard) {
Serial.println("create new audio source: SDCard..");
audio_in = new AudioFileSourceSD(FileToPlay.c_str());
} else
if (currentAudioSource == asSPIFFS) {
Serial.println("create new audio source: SPIFFS..");
audio_in = new AudioFileSourceSPIFFS(FileToPlay.c_str());
} else
if (currentAudioSource == asICY) {
// start wifi
Serial.println("create new audio source: ICY..");
audio_in = new AudioFileSourceICYStream(FileToPlay.c_str());
} else
{
// no audio source found
Serial.println("no audio source found");
return false;
}
if (audio_id3) {
//Serial.println("delete audio id3..");
audio_id3->close();
delete(audio_id3);
audio_id3 = NULL;
}
if (audio_buff) {
//Serial.println("delete audio buffer..");
audio_buff->close();
delete(audio_buff);
audio_buff = NULL;
}
// ++ delete old audio generator +++
// setup buffer and ID3-TAG decoder
audio_buff = new AudioFileSourceBuffer(audio_in, 4096);
audio_buff->RegisterStatusCB(audioStatusCallback, (void*)"buffer");
audio_id3 = new AudioFileSourceID3(audio_buff);
// audio_id3->RegisterMetadataCB(audioMetadataCallback, (void*)"ID3TAG"); ++++ Jim Knopf +++
// set audio generator depending on audio type (MP3, WAV, AAC)
if (currentAudioFormat == afMP3) {
Serial.println("create new audio generator: MP3..");
audio_generator = new AudioGeneratorMP3;
} else
if (currentAudioFormat == afWAV) {
Serial.println("create new audio generator: WAV..");
audio_generator = new AudioGeneratorWAV;
} else
if (currentAudioFormat == afAAC) {
Serial.println("create new audio generator: AAC..");
audio_generator = new AudioGeneratorAAC;
} else
{
// no audio generator found
Serial.println("no audio generator found");
return false;
}
// wakeup amplifier/sd card/neopixel
Serial.println("wakeup the amplifier/SD-card..");
pinMode(PIN_DAC_SD, OUTPUT);
digitalWrite(PIN_DAC_SD, HIGH);
delay(10);
// seek to last position
if (filepos > 0) {
audio_id3->seek(filepos, SEEK_SET);
}
else
{
if (audio_id3->getPos() != 0) {
audio_id3->seek(0, SEEK_SET);
}
}
// turn on green LED
pixels.SetPixelColor(0, green);
pixels.Show();
// begin playing
audio_generator->begin(audio_id3, audio_out);
config.currentPlaylist = currentPlaylist;
config.currentFileIndex = currentFileIndex;
config.currentPos = filepos;
Serial.println("Play file: STARTED, filename: '" + String(FileToPlay) + "'; filepos: " + String(filepos) + "; Free heap: " + String(ESP.getFreeHeap()));
return true;
}
/*
* play first audio file in current directory
*/
void playFirstFile(){
Serial.println("Play first file");
stopPlaying();
int nextFileIndex = 0;
config.currentFileIndex = 0;
Serial.println("Play first file from " + config.currentPlaylist + ", Index: " + String(nextFileIndex));
playFile(config.currentPlaylist, nextFileIndex, 0);
}
/*
* play next audio file in current directory
*/
bool playNextFile(){
Serial.println("Play next file");
stopPlaying();
int nextFileIndex = config.currentFileIndex + 1;
config.currentFileIndex = nextFileIndex;
Serial.println("Play next file from " + config.currentPlaylist + ", Index: " + String(nextFileIndex));
return playFile(config.currentPlaylist, nextFileIndex, 0);
}
/*
* play previous audio file in current directory
*/
bool playPreviousFile(){
Serial.println("Play previous file");
stopPlaying();
int prevFileIndex = config.currentFileIndex - 1;
if (prevFileIndex < 0) {
prevFileIndex = 0;
}
config.currentFileIndex = prevFileIndex;
Serial.println("Play previous file from " + config.currentPlaylist + ", Index: " + String(prevFileIndex));
return playFile(config.currentPlaylist, prevFileIndex, 0);
}
/*
* Async Webserver events
*/
void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){
if(type == WS_EVT_CONNECT){
Serial.printf("ws[%s][%u] connect\n", server->url(), client->id());
client->printf("Hello Client %u :)", client->id());
client->ping();
} else if(type == WS_EVT_DISCONNECT){
Serial.printf("ws[%s][%u] disconnect: %u\n", server->url(), client->id());
} else if(type == WS_EVT_ERROR){
Serial.printf("ws[%s][%u] error(%u): %s\n", server->url(), client->id(), *((uint16_t*)arg), (char*)data);
} else if(type == WS_EVT_PONG){
Serial.printf("ws[%s][%u] pong[%u]: %s\n", server->url(), client->id(), len, (len)?(char*)data:"");
} else if(type == WS_EVT_DATA){
AwsFrameInfo * info = (AwsFrameInfo*)arg;
String msg = "";
if(info->final && info->index == 0 && info->len == len){
//the whole message is in a single frame and we got all of it's data
Serial.printf("ws[%s][%u] %s-message[%llu]: ", server->url(), client->id(), (info->opcode == WS_TEXT)?"text":"binary", info->len);
if(info->opcode == WS_TEXT){
for(size_t i=0; i < info->len; i++) {
msg += (char) data[i];
}
} else {
char buff[3];
for(size_t i=0; i < info->len; i++) {
sprintf(buff, "%02x ", (uint8_t) data[i]);
msg += buff ;
}
}
Serial.printf("%s\n",msg.c_str());
if(info->opcode == WS_TEXT)
client->text("I got your text message");
else
client->binary("I got your binary message");
} else {
//message is comprised of multiple frames or the frame is split into multiple packets
if(info->index == 0){
if(info->num == 0)
Serial.printf("ws[%s][%u] %s-message start\n", server->url(), client->id(), (info->message_opcode == WS_TEXT)?"text":"binary");
Serial.printf("ws[%s][%u] frame[%u] start[%llu]\n", server->url(), client->id(), info->num, info->len);
}
Serial.printf("ws[%s][%u] frame[%u] %s[%llu - %llu]: ", server->url(), client->id(), info->num, (info->message_opcode == WS_TEXT)?"text":"binary", info->index, info->index + len);
if(info->opcode == WS_TEXT){
for(size_t i=0; i < info->len; i++) {
msg += (char) data[i];
}
} else {
char buff[3];
for(size_t i=0; i < info->len; i++) {
sprintf(buff, "%02x ", (uint8_t) data[i]);
msg += buff ;
}
}
Serial.printf("%s\n",msg.c_str());
if((info->index + len) == info->len){
Serial.printf("ws[%s][%u] frame[%u] end[%llu]\n", server->url(), client->id(), info->num, info->len);
if(info->final){
Serial.printf("ws[%s][%u] %s-message end\n", server->url(), client->id(), (info->message_opcode == WS_TEXT)?"text":"binary");
if(info->message_opcode == WS_TEXT)
client->text("I got your text message");
else
client->binary("I got your binary message");
}
}
}
}
}
void handleNotFound(AsyncWebServerRequest *request){
Serial.printf("NOT_FOUND: ");
if(request->method() == HTTP_GET)
Serial.printf("GET");
else if(request->method() == HTTP_POST)
Serial.printf("POST");
else if(request->method() == HTTP_DELETE)
Serial.printf("DELETE");
else if(request->method() == HTTP_PUT)
Serial.printf("PUT");
else if(request->method() == HTTP_PATCH)
Serial.printf("PATCH");
else if(request->method() == HTTP_HEAD)
Serial.printf("HEAD");
else if(request->method() == HTTP_OPTIONS)
Serial.printf("OPTIONS");
else
Serial.printf("UNKNOWN");
Serial.printf(" http://%s%s\n", request->host().c_str(), request->url().c_str());
if(request->contentLength()){
Serial.printf("_CONTENT_TYPE: %s\n", request->contentType().c_str());
Serial.printf("_CONTENT_LENGTH: %u\n", request->contentLength());
}
int headers = request->headers();
int i;
for(i=0;i<headers;i++){
AsyncWebHeader* h = request->getHeader(i);
Serial.printf("_HEADER[%s]: %s\n", h->name().c_str(), h->value().c_str());
}
int params = request->params();
for(i=0;i<params;i++){
AsyncWebParameter* p = request->getParam(i);
if(p->isFile()){
Serial.printf("_FILE[%s]: %s, size: %u\n", p->name().c_str(), p->value().c_str(), p->size());
} else if(p->isPost()){
Serial.printf("_POST[%s]: %s\n", p->name().c_str(), p->value().c_str());
} else {
Serial.printf("_GET[%s]: %s\n", p->name().c_str(), p->value().c_str());
}
}
Serial.println("Free heap: " + String(ESP.getFreeHeap()));
request->send(404);
}
/*
* gets called when WiFiManager enters configuration mode
*/
void configModeCallback (AsyncWiFiManager *myWiFiManager) {
Serial.println("Entered config mode");
Serial.println(WiFi.softAPIP());
// if you used auto generated SSID, print it
Serial.println(myWiFiManager->getConfigPortalSSID());
// entered config mode, make led toggle faster
ticker.attach(0.2, tick);
}
/*
* ticker event for blue blinking LED
*/
void tick()
{
// toggle blue LED
togggleState = !togggleState;
digitalWrite(MYLED_BUILTIN, togggleState); // set pin to the opposite state
if (togggleState) {
pixels.SetPixelColor(0, blue);
}
else {
pixels.SetPixelColor(0, black);
}
pixels.Show();
}
/*
* Method to print the reason by which ESP32 has been awaken from sleep
*/
void print_wakeup_reason(){
esp_sleep_wakeup_cause_t wakeup_reason;
wakeup_reason = esp_sleep_get_wakeup_cause();
switch(wakeup_reason)
{
case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
}
}
/*
* Fast reset method for RFID reader
*/
void mfrc522_fast_Reset()
{
digitalWrite(PIN_RFID_RST, HIGH);
rfid_522.PCD_Reset();
rfid_522.PCD_WriteRegister(rfid_522.TModeReg, 0x80); // TAuto=1; timer starts automatically at the end of the transmission in all communication modes at all speeds
rfid_522.PCD_WriteRegister(rfid_522.TPrescalerReg, 0x43); // 10μs.
rfid_522.PCD_WriteRegister(rfid_522.TReloadRegH, 0x00); // Reload timer with 0x064 = 30, ie 0.3ms before timeout.
rfid_522.PCD_WriteRegister(rfid_522.TReloadRegL, 0x1E);
//rfid_522.PCD_WriteRegister(rfid_522.TReloadRegL, 0x1E);
rfid_522.PCD_WriteRegister(rfid_522.TxASKReg, 0x40); // Default 0x00. Force a 100 % ASK modulation independent of the ModGsPReg register setting
rfid_522.PCD_WriteRegister(rfid_522.ModeReg, 0x3D); // Default 0x3F. Set the preset value for the CRC coprocessor for the CalcCRC command to 0x6363 (ISO 14443-3 part 6.2.4)
rfid_522.PCD_AntennaOn(); // Enable the antenna driver pins TX1 and TX2 (they were disabled by the reset)
}
/*
* Show battery voltage
*/
void printBatteryVoltage()
{
// Battery Voltage
float VBAT = (127.0f/100.0f) * 3.30f * float(analogRead(PIN_VBAT)) / 4095.0f; // LiPo battery
Serial.print("Battery Voltage = "); Serial.print(VBAT, 2); Serial.println(" V");
}
AsyncWebHandler SDWebHandler;
//AsyncWebHandler WSHandler;
/*
* start WIFI
*/
void startWIFI()
{
// Wifi already started ?
if (WiFiStarted) {
Serial.println("Wifi started.");
return;
}
WiFiStarted = true;
Serial.println("starting Wifi.");
// shutdown amplifier
bool wasAmplifierStarted = digitalRead(PIN_DAC_SD);
digitalWrite(PIN_DAC_SD, HIGH);
///WiFi manager
//Local intialization. Once its business is done, there is no need to keep it around
AsyncWiFiManager wifiManager(&server, &dns);
// sets timeout until configuration portal gets turned off
wifiManager.setTimeout(180);
// start ticker with 0.6 because we start in AP mode and try to connect
ticker.attach(0.6, tick);
// turn on blue LED
pixels.SetPixelColor(0, blue);
uint8_t i;
for(i=1; i <= (NEOPIXEL_NUM); i++) {
pixels.SetPixelColor(i, black);
}
pixels.Show();
//set callback that gets called when connecting to previous WiFi fails, and enters Access Point mode
wifiManager.setAPCallback(configModeCallback);
if (!wifiManager.autoConnect(hostname)) {
Serial.println("failed to connect and hit timeout, restarting");
//reset and try a, or maybe put it to deep sleep
ESP.restart();
delay(3000);
return;
}
// check connection is established
while (WiFi.status() != WL_CONNECTED) {
Serial.println("...Connecting to WiFi");
delay(1000);
}
WiFi.setHostname(hostname);
// if you get here you have connected to the WiFi
Serial.print("Connected! Host: ");
Serial.print(hostname);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
//reset settings - for testing
// wifiManager.resetSettings();
/*esp_wifi_restore();
delay(1000);
ESP.restart();
*/
// show signal strength
long rssi = WiFi.RSSI();
Serial.print("RSSI:");
Serial.println(rssi);
// stop blinking, turn on blue LED
ticker.detach();
digitalWrite(MYLED_BUILTIN, LOW);
pixels.SetPixelColor(0, blue);
pixels.Show();
// Start mDNS service so we can connect to http://pietbox.local (if Bonjour installed on Windows)
if (MDNS.begin(hostname)) {
MDNS.addService("http", "tcp", 80);
Serial.println("MDNS responder started");
Serial.println("You can now connect to http://" + String(hostname) + ".local");
}
// connect HTTP server events
// websocket
/* ws.onEvent(onWsEvent);
WSHandler = server.addHandler(&ws);
events.onConnect([](AsyncEventSourceClient *client){
client->send("hello!", NULL, millis(), 1000);
});
server.addHandler(&events);
*/
// default file is index.html
server.serveStatic("/", SDFileSystem, "/").setDefaultFile("index.htm");
// SD file system editor
SDWebHandler = server.addHandler(new SDEditor(SDFileSystem, http_username, http_password));
// get free heap
server.on("/heap", HTTP_GET, [](AsyncWebServerRequest *request){
Serial.println("HTTP: request free heap");
AsyncResponseStream *response = request->beginResponseStream("text/plain");
response->addHeader("Access-Control-Allow-Origin", "*"); // to allow usage from local web page for test & development
response->printf( String(ESP.getFreeHeap()).c_str() );
request->send(response);
});
// get battery voltage
server.on("/vbatabs", HTTP_GET, [](AsyncWebServerRequest *request){
Serial.println("HTTP: request battery charge in volt");
float VBAT = (127.0f/100.0f) * 3.30f * float(analogRead(PIN_VBAT)) / 4095.0f; // LiPo battery in volt
request->send(200, "text/plain", String(VBAT));
});
// get battery in percent
server.on("/vbat", HTTP_GET, [](AsyncWebServerRequest *request){
float VBAT = (127.0f/100.0f) * 3.30f * float(analogRead(PIN_VBAT)) / 4095.0f; // LiPo battery in volt
uint8_t percent = constrain(int(((VBAT - VBAT_MIN) / (VBAT_MAX - VBAT_MIN)) * 100.0f), 0, 100); // 0% = VBAT_MIN (3.0V) - 100% = VBAT_MAX (4.2V)
if (digitalRead(PIN_VUSB)) {
Serial.println("HTTP: request battery charge: " + String(percent) + " % - charging..");
request->send(200, "text/plain", String(percent) + " % - charging..");
} else {
Serial.println("HTTP: request battery charge: " + String(percent) + " %");
request->send(200, "text/plain", String(percent) + " %");
}
});
// reset
server.on("/reset", HTTP_POST, [](AsyncWebServerRequest *request){
Serial.println("HTTP: request reset..");
Serial.println("resetting..");
request->send(200, "text/plain", "Reset and restarting, please wait..");
delay(2000);
ESP.restart();
});
// reset WIFI settings
server.on("/resetwifi", HTTP_GET, [](AsyncWebServerRequest *request){
Serial.println("HTTP: request reset WIFI..");
request->send(200, "text/plain","Reset WIFI and restarting, please wait..");
WiFi.enableSTA(true);
WiFi.persistent(true);
WiFi.disconnect(true);
WiFi.persistent(false);
delay(2000);
ESP.restart();
});
// handle current track metadata
server.on("/currentTitle", HTTP_GET, [](AsyncWebServerRequest *request){
Serial.println("HTTP: request currentTitle: '" + currentTitle + "'");
request->send(200, "text/plain", currentTitle);
});
server.on("/currentPerformer", HTTP_GET, [](AsyncWebServerRequest *request){
Serial.println("HTTP: request currentPerformer: '" + currentPerformer + "'");
request->send(200, "text/plain", currentPerformer);
});
server.on("/currentAlbum", HTTP_GET, [](AsyncWebServerRequest *request){
Serial.println("HTTP: request currentAlbum: '" + currentAlbum + "'");
request->send(200, "text/plain", currentAlbum);
});
server.on("/currentVolume", HTTP_GET, [](AsyncWebServerRequest *request){
Serial.println("HTTP: request currentVolume: '" + String(config.currentVolume) + "'");
request->send(200, "text/plain", String(config.currentVolume));
});
// handle not found
server.onNotFound(handleNotFound);
// handle file upload
server.onFileUpload([](AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final){
if(!index)
Serial.printf("UploadStart: %s\n", filename.c_str());
Serial.printf("%s", (const char*)data);
if(final)
Serial.printf("UploadEnd: %s (%u)\n", filename.c_str(), index+len);
});
// handle request body
server.onRequestBody([](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total){
if(!index)
Serial.printf("BodyStart: %u\n", total);
Serial.printf("%s", (const char*)data);
if(index + len == total)
Serial.printf("BodyEnd: %u\n", total);
});
// start HTTP server
server.begin();
Serial.println("HTTP server started");
// reactivate amplifier?
if (wasAmplifierStarted) {
Serial.println("wakeup amplifier/SD-card");
digitalWrite(PIN_DAC_SD, HIGH);
}
}
/*
* stop WIFI
*/
void stopWIFI(){
// Wifi already started ?
if (!WiFiStarted) {
return;
}
Serial.println("stop webserver..");
// remove events
server.onNotFound(NULL);
server.onFileUpload(NULL);
server.onRequestBody(NULL);
/* events.onConnect(NULL);
ws.onEvent(NULL);
server.removeHandler(&WSHandler); +++
server.removeHandler(&events);
*/
// remove handlers
server.removeHandler(&SDWebHandler);
// reset HTTP server
server.reset();
Serial.println("disable Wifi..");
// stop MDNS
MDNS.end();
WiFi.disconnect(false);
// turn off blue LED
pixels.SetPixelColor(0, black);
pixels.Show();
WiFiStarted = false;
}
#define DEBUG
#ifdef DEBUG
#define DEBUG_PRINT(x) Serial.println (x)
#else
#define DEBUG_PRINT(x)
#endif
/***************************************
* Setup
***************************************/
void setup(void){
#ifdef DEBUG
// Initialize serial communications with the PC
Serial.begin(115200);
Serial.println("");
Serial.println("");
Serial.println(" ###### ");
Serial.println(" # # # ###### ##### ##### #### # #");
Serial.println(" # # # # # # # # # # # ");
Serial.println(" ###### # ##### # ##### # # ## ");
Serial.println(" # # # # # # # # ## ");
Serial.println(" # # # # # # # # # # ");
Serial.println(" # # ###### # ##### #### # #");
Serial.println("");
Serial.println("");
#endif
// turn Wifi/BLE off
Serial.println("turn Wifi/BLE off..");
WiFi.mode(WIFI_OFF);
// btStop();
//Increment boot number and print it every reboot
++sleepCount;
#ifdef DEBUG
Serial.println("Sleep number: " + String(sleepCount));
Serial.println("Free heap: " + String(ESP.getFreeHeap()));
Serial.println("SDK version: " + String(ESP.getSdkVersion()));
//Print the wakeup reason for ESP32
print_wakeup_reason();
#endif
/*
First we configure the wake up source
We set our ESP32 to wake up every short/middle/long seconds
*/
esp_sleep_enable_ext0_wakeup(PIN_ENCODER_BUTTON, 0); //1 = High, 0 = Low
int timeToSleep = TIME_TO_SLEEP_SHORT;
if (sleepCount > 30)
timeToSleep = TIME_TO_SLEEP_MIDDLE;
if (sleepCount > 60)
timeToSleep = TIME_TO_SLEEP_LONG;
if (sleepCount > (60 * 60))
timeToSleep = TIME_TO_SLEEP_VERY_LONG;
esp_sleep_enable_timer_wakeup(timeToSleep * 1000000);
#ifdef DEBUG
Serial.println("Setup ESP32 to sleep for every " + String(timeToSleep) + " Seconds");
#endif
/*
Next we decide what all peripherals to shut down/keep on
By default, ESP32 will automatically power down the peripherals
not needed by the wakeup source, but if you want to be a poweruser
this is for you. Read in detail at the API docs
http://esp-idf.readthedocs.io/en/latest/api-reference/system/deep_sleep.html
Left the line commented as an example of how to configure peripherals.
The line below turns off all RTC peripherals in deep sleep.
*/
// esp_deep_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
//Serial.println("Configured all RTC Peripherals to be powered down in sleep");
// check for external power supply
hasExternalPower = digitalRead(PIN_VUSB);
if (!hasExternalPower) {
// Print battery voltage
printBatteryVoltage();
}
// turn on internal LED
pinMode(MYLED_BUILTIN, OUTPUT);
digitalWrite(MYLED_BUILTIN, LOW);
// Initialize the NeoPixel library and turn off the stripe
Serial.println("Init neopixel..");
pixels.Begin();
pixels.Show();
// sleeping amplifier, neopixel & SD card power
pinMode(PIN_DAC_SD, OUTPUT);
digitalWrite(PIN_DAC_SD, LOW);
// VUSB power with internal pulldown
pinMode(PIN_VUSB, INPUT_PULLDOWN);
// RESET pin for RFID reader
pinMode(PIN_RFID_RST, OUTPUT);
digitalWrite(PIN_RFID_RST, LOW);
// switch heartbeat LED OFF
digitalWrite(MYLED_BUILTIN, HIGH);
/*
Now that we have setup a wake cause and if needed setup the
peripherals state in deep sleep, we can now start going to
deep sleep.
In the case that no wake up sources were provided but deep
sleep was started, it will sleep forever unless hardware
reset occurs.
*/
if ((readerType == rtUnknown) || (readerType == rtMFRC522)) {
// Init MFRC522
Serial.println("Try to detect MFRC522 reader..");
SPI.begin();
delay(10);
rfid_522.PCD_Init();
// If you set Antenna Gain to Max it will increase reading distance
// BUT you will lose reading ability when tag is right on the sensor!
// rfid_522.PCD_SetAntennaGain(rfid_522.RxGain_max);
// delay(10);
// Get the MFRC522 software version
byte v = rfid_522.PCD_ReadRegister(rfid_522.VersionReg);
Serial.print(F("MFRC522 Software Version: 0x"));
Serial.print(v, HEX);
if (v == 0x91)
Serial.print(F(" = v1.0"));
else if (v == 0x92)
Serial.print(F(" = v2.0"));
else
Serial.print(F(" (unknown)"));
Serial.println("");
if ((v == 0x91) || (v == 0x92))
readerType = rtMFRC522;
}
// check for PN532 reader
if ((readerType == rtUnknown) || (readerType == rtPN532)) {
Serial.print("Try to detect PN532 reader..");
digitalWrite(PIN_RFID_RST, HIGH);
delay(100);
rfid_532.begin();
uint32_t versiondata = rfid_532.getFirmwareVersion();
if (versiondata) {
readerType = rtPN532;
} else {
Serial.println("Didn't find PN53x board");
}
// Got ok data, print it out!
Serial.print("Found chip PN5"); Serial.println((versiondata>>24) & 0xFF, HEX);
Serial.print("Firmware ver. "); Serial.print((versiondata>>16) & 0xFF, DEC);
Serial.print('.'); Serial.println((versiondata>>8) & 0xFF, DEC);
Serial.println(' ');
// Set the max number of retry attempts to read from a card
// This prevents us from waiting forever for a card, which is
// the default behaviour of the PN532.
rfid_532.setPassiveActivationRetries(0xFF);
// configure board to read RFID tags
rfid_532.SAMConfig();
}
// check RFID is available
if (sleepCount < 2) {
if (readerType == rtUnknown) {
Serial.println("RFID-reader failed, start WIFI..");
startWIFI();
}
}
uid = "";
// get the RFID reader card status
if (readerType == rtMFRC522) {
rfid_522.PICC_ReadCardSerial(); //Always fails
rfid_522.PICC_IsNewCardPresent(); //Does RequestA
if ((!rfid_522.PICC_ReadCardSerial()) && (!WiFiStarted)) { //Okay. This does the same PICC_Select as the previous ReadCardSerial(), but this one fails if there is no card on the reader. Funny.
Serial.println("PCD_SoftPowerDown");
rfid_522.PCD_SoftPowerDown();
// stop SPI
SPI.end();
// turn Wifi/BLE off
Serial.println("turn Wifi/BLE off");
WiFi.mode(WIFI_OFF);
// btStop();
// esp_wifi_stop();
// esp_wifi_deinit();
Serial.println("Going to sleep now");
// close serial
Serial.end();
// put neopixel pin to input to save current
pinMode(PIN_NEOPIXEL_DIN, INPUT);
// turn off power to SD card/DAC
digitalWrite(PIN_DAC_SD, LOW);
esp_deep_sleep_start();
// ESP is sleeping now!
}
// Wake up now !
Serial.println("PCD dump:");
rfid_522.PCD_DumpVersionToSerial();
// Get PICC's UID and store on a variable
for (byte i = 0; i < rfid_522.uid.size; i++) {
uid += String(rfid_522.uid.uidByte[i], HEX);
}
};
if (readerType == rtPN532) {
// Wait for an ISO14443A type cards (Mifare, etc.). When one is found
// 'uid' will be populated with the UID, and uidLength will indicate
// if the uid is 4 bytes (Mifare Classic) or 7 bytes (Mifare Ultralight)
uint8_t uidLength; // Length of the UID (4 or 7 bytes depending on ISO14443A card type)
uint8_t myuid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID
boolean success;
Serial.println("PN532: ISO14443A read passive target id..");
success = rfid_532.readPassiveTargetID(PN532_MIFARE_ISO14443A, &myuid[0], &uidLength, 100);
if (!success) {
Serial.println("PN532: ISO14443B read passive target id..");
success = rfid_532.readPassiveTargetID(PN532_ISO14443B, &myuid[0], &uidLength, 100);
}
if (!success) {
Serial.println("Going to sleep now");
// close serial
Serial.end();
//rfid_532.PCD_SoftPowerDown(); ++ shutdown ++
// stop SPI
SPI.end();
// turn Wifi/BLE off
WiFi.mode(WIFI_OFF);
//btStop();
esp_wifi_stop();
// hard reset the RFID reader
digitalWrite(PIN_RFID_RST, LOW);
// put neopixel pin to input to save current
pinMode(PIN_NEOPIXEL_DIN, INPUT);
// turn off power to SD card/DAC
digitalWrite(PIN_DAC_SD, LOW);
esp_wifi_deinit();
esp_deep_sleep_start();
// ESP is sleeping now!
}
// Get PICC's UID and store on a variable
for (byte i = 0; i < uidLength; i++) {
uid += String(myuid[i], HEX);
}
}
uid.trim();
uid.toUpperCase();
card_present = (uid!="");
if (card_present)
Serial.println("RFID-UID: '" + String(uid) + "'");
else
Serial.println("no card detected");
// wakeup
sleepCount = 0;
/*
//Send OTA events to the browser
ArduinoOTA.onStart([]() { events.send("Update Start", "ota"); });
ArduinoOTA.onEnd([]() { events.send("Update End", "ota"); });
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
char p[32];
sprintf(p, "Progress: %u%%\n", (progress/(total/100)));
events.send(p, "ota");
});
ArduinoOTA.onError([](ota_error_t error) {
if(error == OTA_AUTH_ERROR) events.send("Auth Failed", "ota");
else if(error == OTA_BEGIN_ERROR) events.send("Begin Failed", "ota");
else if(error == OTA_CONNECT_ERROR) events.send("Connect Failed", "ota");
else if(error == OTA_RECEIVE_ERROR) events.send("Recieve Failed", "ota");
else if(error == OTA_END_ERROR) events.send("End Failed", "ota");
});
ArduinoOTA.setHostname(hostname);
ArduinoOTA.begin();
*/
/*
Create the audio classes
*/
Serial.println("initialize audio objects..");
audio_in = NULL;
audio_buff = NULL;
audio_id3 = NULL;
audio_out = new AudioOutputI2S();
audio_out->SetOutputModeMono(true);
audio_generator = new AudioGeneratorMP3;
// wakeup amplifier
Serial.println("wakeup the amplifier/SD-card..");
digitalWrite(PIN_DAC_SD, HIGH);
delay(2);
Serial.println("Init SD card");
SPI2.begin(PIN_SD_CARD_SCK, PIN_SD_CARD_MISO, PIN_SD_CARD_MOSI, PIN_SD_CARD_SS); // SCK, MISO, MOSI, SS
delay(10);
int TryCounter = 0;
int state = 0;
// check multiple times for valid SD card
while ((!SD.begin(PIN_SD_CARD_SS, SPI2, 4000000)) && (TryCounter < 10)) {
// while ((!SD.begin(PIN_SD_CARD_SS, SPI2, 80000000)) && (TryCounter < 10)) { // 80000000 sometimes fails
Serial.println(F("Failed to initialize SD library"));
state = !state;
if (state) {
pixels.SetPixelColor(0, red);
}
else {
pixels.SetPixelColor(0, black);
}
pixels.Show();
TryCounter++;
delay(200);
}
if (TryCounter < 10) {
Serial.println("SD Card initialized.");
hasSD = true;
uint8_t cardType = SD.cardType();
if(cardType == CARD_NONE){
Serial.println("No SD card attached");
return;
}
Serial.print("SD Card Type: ");
if(cardType == CARD_MMC){
Serial.println("MMC");
} else if(cardType == CARD_SD){
Serial.println("SDSC");
} else if(cardType == CARD_SDHC){
Serial.println("SDHC");
} else {
Serial.println("UNKNOWN");
}
uint64_t cardSize = SD.cardSize() / (1024 * 1024);
Serial.printf("SD Card Size: %lluMB\n", cardSize);
// Should load default config if run for the first time
Serial.println(F("Loading configuration..."));
loadConfiguration(filename, config);
// set default/last save volume
if (config.currentVolume > stepsPerTurn) config.currentVolume = stepsPerTurn;
// startup with minimum volume
if (config.currentVolume < 2) config.currentVolume = 2;
Serial.println("Set volume to " + String(config.currentVolume));
setVolume(config.currentVolume);
// initialize the encoder pushbutton pin as an input (internal pullup not available for gpio 34-39)
pinMode(PIN_ENCODER_BUTTON, INPUT);
// pushbutton pressed? start the WLAN
if (!digitalRead(PIN_ENCODER_BUTTON)) {
startWIFI();
}
else
{
// check playlist exists for the UID, if not create it
String currentPlaylist = "/" + uid + ".m3u";
if ((!SD.exists(currentPlaylist) && (uid != ""))) {
Serial.println("No playlist found for UID " + uid);
File myFile = SD.open(currentPlaylist, FILE_WRITE);
myFile.close();
Serial.println("New playlist created for UID " + uid);
startWIFI();
}
if (!(currentPlaylist == config.currentPlaylist)) {
Serial.println("Play a new playlist " + currentPlaylist);
config.currentPlaylist = currentPlaylist;
config.currentFileIndex = 0;
config.currentPos = 0;
}
// play first file
if (!playFile(config.currentPlaylist, config.currentFileIndex, config.currentPos)) {
config.currentFileIndex = 0;
config.currentPos = 0;
playFile(config.currentPlaylist, config.currentFileIndex, config.currentPos);
}
}
}
else
{
Serial.println("SD Card not initialized.");
Serial.flush();
// only goto sleep if a rfid reader is connected
if (readerType != rtUnknown) {
// close serial
Serial.end();
digitalWrite(MYLED_BUILTIN, LOW);
// turn on red LED
pixels.SetPixelColor(0, black);
pixels.Show();
// put neopixel pin to input to save current
pinMode(PIN_NEOPIXEL_DIN, INPUT);
// turn off power to SD card/DAC
digitalWrite(PIN_DAC_SD, LOW);
esp_wifi_stop();
esp_wifi_deinit();
esp_deep_sleep_start();
// ESP is sleeping now!
}
}
// Task function for asynchron checking for a NFC card
xTaskCreate(RFIDReaderTaskFunc, /* Task function. */
"RFIDReader", /* String with name of task. */
10000, /* Stack size in words. */
NULL, /* Parameter passed as input of the task */
1, /* Priority of the task. */
NULL); /* Task handle. */
// print free heap after setup
Serial.println("");
Serial.println("Free heap after initialization: " + String(ESP.getFreeHeap()));
Serial.println("");
}
/*
* go to sleep
*/
void goToSleep()
{
// Save configuration file
if (hasSD) {
Serial.println(F("Saving configuration.."));
saveConfiguration(filename, config);
}
// turn off the RFID reader
Serial.println("soft powerdown RFID-reader..");
if (readerType == rtMFRC522) {
rfid_522.PCD_SoftPowerDown();
} else
if (readerType == rtPN532) {
digitalWrite(PIN_RFID_RST, LOW);
};
// turn off SD card
Serial.println("close SD-card..");
SD.end();
// stop SPI
SPI2.end();
SPI.end();
digitalWrite(PIN_SD_CARD_SS, LOW);
// shutdown amplifier
Serial.println("shutdown amplifier/SD-card..");
digitalWrite(PIN_DAC_SD, LOW);
// turn of neopixel LED
Serial.println("turn of neopixel LED..");
uint8_t i;
for(i=0; i <= (NEOPIXEL_NUM); i++) {
pixels.SetPixelColor(i, black);
}
pixels.Show();
// turn off WIFI
Serial.println(" turn off WIFI..");
WiFi.disconnect(true); //esp_wifi_stop();
// turn Wifi/BLE off
WiFi.mode(WIFI_OFF);
//btStop();
// esp_wifi_stop(); saw a crash here. Properly stop Wifi & Bluetooth to save power +++
// esp_wifi_deinit();
// going into deep sleep
Serial.println("Going to sleep now");
// close serial
Serial.end();
delay(50); //time to write to serial
// put neopixel pin to input to save current
pinMode(PIN_NEOPIXEL_DIN, INPUT);
// turn off power to SD card/DAC
digitalWrite(PIN_DAC_SD, LOW);
esp_deep_sleep_start();
// ESP is sleeping now!
Serial.println("This will never be printed");
}
/*
* RFID reader task
*/
void RFIDReaderTaskFunc( void * parameter )
{
Serial.println("start RFIDReader task");
// check RFID reader is available
while (!shutdownFlag) {
if (readerType == rtUnknown) {
// RFID reader not available, check for MFRC522
rfid_522.PCD_Init();
// Get the MFRC522 software version
byte v = rfid_522.PCD_ReadRegister(rfid_522.VersionReg);
Serial.print(F("MFRC522 Software Version: 0x"));
Serial.println(v, HEX);
if ((v == 0x91) || (v == 0x92)) {
readerType = rtMFRC522;
}
}
card_present = false;
if (readerType == rtMFRC522) {
//Serial.println("MFRC522: PICC_ReadCardSerial");
rfid_522.PICC_ReadCardSerial(); // Always fails
//Serial.println("MFRC522: PICC_IsNewCardPresent");
rfid_522.PICC_IsNewCardPresent(); // Does RequestA
// check RFID-reader and card is present
card_present = rfid_522.PICC_ReadCardSerial();
//Serial.println("MFRC522: card_present: " + card_present);
} else
if (readerType == rtPN532) {
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 }; // Buffer to store the returned UID
uint8_t uidLength; // Length of the UID (4 or 7 bytes depending on ISO14443A card type)
card_present = rfid_532.readPassiveTargetID(PN532_MIFARE_ISO14443A, &uid[0], &uidLength, 100);
if (!card_present) {
card_present = rfid_532.readPassiveTargetID(PN532_JEWEL, &uid[0], &uidLength, 100);
}
}
if (!card_present) {
Serial.println("vTask (RFIDReader): card removed");
card_present = false;
if (audio_generator && (audio_generator->isRunning())) {
// save current audio file, position and volume ++
unsigned int p = audio_in->getPos();
Serial.println("get current filepos: " + String(p));
config.currentPos = p;
}
else
{
config.currentPos = 0;
}
// goToSleep is done in loop thread
shutdownFlag = true;
}
delay(1000);
}
Serial.println("delete RFIDReader task");
vTaskDelete( NULL );
}
void setVolume(int newVolume){
// ensure volume is in range
newVolume = constrain(newVolume, 0, stepsPerTurn);
config.currentVolume = newVolume;
Serial.println("Volume " + String(config.currentVolume));
Serial.println("Free heap: " + String(ESP.getFreeHeap()));
myEnc.write(config.currentVolume);
// set gain
audio_out->SetGain(((float)config.currentVolume)/stepsPerTurn * maxVolume);
// show current volume on neopixel ring
uint32_t neopixelValue = map(newVolume, 0, stepsPerTurn, 0, (NEOPIXEL_NUM) * 255);
uint8_t fullLEDs = (neopixelValue / 255);
uint8_t lastLED = (neopixelValue % 255);
uint8_t i;
// clear all LED's
for(i=0; i < (NEOPIXEL_NUM); i++) {
pixels.SetPixelColor(i + 1, black);
}
// set "full" LED's
for(i=0; i < (fullLEDs); i++) {
pixels.SetPixelColor(i + 1, RgbColor(255));
}
// set last LED
if (lastLED != 0) {
pixels.SetPixelColor(fullLEDs + 1, RgbColor(lastLED));
}
pixels.Show();
// start fade out after 2000 ms
startTimeNeoPixel = millis();
}
/* *************************************
* Loop
***************************************/
void loop(void){
// handle rotary encoder
long newVolume = myEnc.read();
if (newVolume != config.currentVolume) {
setVolume(newVolume);
}
// handle over the air updates
// ArduinoOTA.handle();
// handle audio
if ((!shutdownFlag) && (audio_generator) && (audio_generator->isRunning()) && (audio_in->isOpen())) {
if (!audio_generator->loop()) {
if (!playNextFile()) {
Serial.println("Last track, stopped playing.");
stopPlaying();
}
}
}
// handle shutdown
if (shutdownFlag) {
stopPlaying();
stopWIFI();
goToSleep();
}
// handle volume NeoPixels
if (animations.IsAnimating()) {
// the normal loop just needs these two to run the active animations
animations.UpdateAnimations();
pixels.Show();
}
if ((startTimeNeoPixel != 0) && ((millis() - startTimeNeoPixel) > 2000)) {
// starting fade out
Serial.println("start fadeout volume NeoPixels..");
animationState[0].StartingColor = pixels.GetPixelColor(1);
animationState[0].EndingColor = RgbColor(0);
animations.StartAnimation(0, 800, BlendAnimUpdate);
startTimeNeoPixel = 0;
}
if (hasExternalPower != digitalRead(PIN_VUSB)) {
// external USB-power has changed
hasExternalPower = digitalRead(PIN_VUSB);
if (hasExternalPower) {
Serial.println("external power is available now..");
startWIFI();
} else {
Serial.println("external power is gone..");
stopWIFI();
}
}
// pushbutton pressed? start the WLAN
if (encoderButton.pressed()) {
// check for reset command ( > 5 seconds)
if ((lastTimeButtonPressed != 0) && (millis() - lastTimeButtonPressed > 5000)) {
//reset and try a, or maybe put it to deep sleep
ESP.restart();
delay(3000);
return;
}
lastTimeButtonPressed = millis();
Serial.println("Encoder button pressed.");
if (WiFiStarted && (!digitalRead(PIN_VUSB))) {
// handle button with Wifi started
shutdownFlag = true;
} else {
if (!playNextFile()) {
playFirstFile();
}
}
} else
lastTimeButtonPressed = 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment