Started rewrite for using an I2S amplifier with less unnecessary features.
This commit is contained in:
@@ -1,463 +1,13 @@
|
||||
#include "controller.h"
|
||||
#include "main.h"
|
||||
#include "spi_master.h"
|
||||
#include "config.h"
|
||||
#include "playlist.h"
|
||||
#include "http_server.h"
|
||||
#include "updater.h"
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
Controller::Controller(Player* p, PlaylistManager* playlist_manager) {
|
||||
player = p;
|
||||
pm = playlist_manager;
|
||||
_rfid = new MFRC522(17, MFRC522::UNUSED_PIN);
|
||||
void Controller::handle() {
|
||||
|
||||
}
|
||||
|
||||
void Controller::handle_buttons() {
|
||||
|
||||
}
|
||||
|
||||
void Controller::handle_rfid() {
|
||||
|
||||
player->register_controller(this);
|
||||
|
||||
BTN_NEXT_SETUP();
|
||||
BTN_PREV_SETUP();
|
||||
BTN_VOL_UP_SETUP();
|
||||
BTN_VOL_DOWN_SETUP();
|
||||
|
||||
SPIMaster::select_rc522();
|
||||
DEBUG("Initializing RC522...\n");
|
||||
_rfid->PCD_Init();
|
||||
#ifdef SHOW_DEBUG
|
||||
_rfid->PCD_DumpVersionToSerial();
|
||||
#endif
|
||||
SPIMaster::select_rc522(false);
|
||||
INFO("RC522 initialized.\n");
|
||||
|
||||
for (uint8_t i=0; i<NUM_BUTTONS; i++) _button_last_pressed_at[i]=0;
|
||||
}
|
||||
|
||||
void Controller::register_http_server(HTTPServer* h) {
|
||||
_http_server = h;
|
||||
}
|
||||
|
||||
void Controller::loop() {
|
||||
unsigned long now = millis();
|
||||
if ((_last_rfid_scan_at < now - RFID_SCAN_INTERVAL) || (now < _last_rfid_scan_at)) {
|
||||
_check_rfid();
|
||||
_last_rfid_scan_at = now;
|
||||
}
|
||||
if ((_last_position_info_at < now - POSITION_SEND_INTERVAL) || (now < _last_position_info_at)) {
|
||||
send_position();
|
||||
_last_position_info_at = now;
|
||||
}
|
||||
_check_serial();
|
||||
_check_buttons();
|
||||
if (_cmd_queue.length() > 0) {
|
||||
process_message(_cmd_queue);
|
||||
_cmd_queue = "";
|
||||
}
|
||||
|
||||
#ifdef OTA_UPDATE_URL
|
||||
if (!player->is_playing() && _last_update_check_at < now && _last_update_check_at + OTA_CHECK_INTERVAL < now) {
|
||||
Updater::run();
|
||||
} else {
|
||||
_last_update_check_at = now;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!player->is_playing() && !WiFi.isConnected() && _last_wifi_try_at < now && _last_wifi_try_at + 5*60*1000 < now) {
|
||||
wifi_connect();
|
||||
} else {
|
||||
_last_wifi_try_at = now;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t Controller::_get_rfid_card_uid() {
|
||||
SPIMaster::select_rc522();
|
||||
if (!_rfid->PICC_ReadCardSerial()) {
|
||||
if (!_rfid->PICC_IsNewCardPresent()) {
|
||||
return 0;
|
||||
}
|
||||
if (!_rfid->PICC_ReadCardSerial()) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
SPIMaster::select_rc522(false);
|
||||
uint32_t uid = _rfid->uid.uidByte[0]<<24 | _rfid->uid.uidByte[1]<<16 | _rfid->uid.uidByte[2]<<8 | _rfid->uid.uidByte[3];
|
||||
return uid;
|
||||
}
|
||||
|
||||
void Controller::_check_rfid() {
|
||||
//TRACE("check_rfid running...\n");
|
||||
MFRC522::StatusCode status;
|
||||
if (_rfid_present) {
|
||||
byte buffer[2];
|
||||
byte buffer_size = 2;
|
||||
SPIMaster::select_rc522();
|
||||
status = _rfid->PICC_WakeupA(buffer, &buffer_size);
|
||||
if (status == MFRC522::STATUS_OK) {
|
||||
// Card is still present.
|
||||
_rfid->PICC_HaltA();
|
||||
SPIMaster::select_rc522(false);
|
||||
return;
|
||||
}
|
||||
SPIMaster::select_rc522(false);
|
||||
// Card is now gone
|
||||
_rfid_present = false;
|
||||
INFO("No more RFID card.\n");
|
||||
if (_state != LOCKED) {
|
||||
player->stop();
|
||||
}
|
||||
send_controller_status();
|
||||
} else {
|
||||
uint32_t uid = _get_rfid_card_uid();
|
||||
if (uid > 0) {
|
||||
String temp = String(uid, HEX);
|
||||
String s_uid = "";
|
||||
for (int i=0; i<(8-temp.length()); i++) {
|
||||
s_uid.concat("0");
|
||||
}
|
||||
s_uid.concat(temp);
|
||||
INFO("New RFID card uid: %s\n", s_uid.c_str());
|
||||
_last_rfid_uid = s_uid;
|
||||
_rfid_present = true;
|
||||
|
||||
String data = _read_rfid_data();
|
||||
_last_rfid_data = data;
|
||||
|
||||
Playlist* pl = pm->get_playlist_for_id(s_uid);
|
||||
if (data.indexOf("[lock]") != -1) {
|
||||
if (_state == LOCKED) {
|
||||
_state = NORMAL;
|
||||
DEBUG("ControllerState is now UNLOCKED\n");
|
||||
} else {
|
||||
DEBUG("ControllerState is now LOCKING\n");
|
||||
_state = LOCKING;
|
||||
}
|
||||
}
|
||||
if (pl==NULL) {
|
||||
INFO("Could not find album for id '%s'.\n", s_uid.c_str());
|
||||
send_controller_status();
|
||||
return;
|
||||
}
|
||||
int index;
|
||||
if (data.indexOf("[advent]") != -1 && pl->is_fresh()) {
|
||||
struct tm time;
|
||||
getLocalTime(&time);
|
||||
if (time.tm_mon == 11) { // tm_mon is "months since january", so 11 means december.
|
||||
pl->advent_shuffle(time.tm_mday);
|
||||
} else {
|
||||
DEBUG("Album is in advent mode, but it isn't december (yet). Not playing.\n");
|
||||
return;
|
||||
}
|
||||
} else if (data.indexOf("[random]") != -1 && pl->is_fresh()) {
|
||||
pl->shuffle();
|
||||
} else if ((index=data.indexOf("[random:")) != -1 && pl->is_fresh()) {
|
||||
String temp = data.substring(index + 8);
|
||||
index = temp.indexOf("]");
|
||||
TRACE("temp: %s, temp.substring(0, %d): %s\n", temp.c_str(), index, temp.substring(0, index).c_str());
|
||||
if (index>0) {
|
||||
uint8_t random_offset = temp.substring(0, index).toInt();
|
||||
pl->shuffle(random_offset);
|
||||
}
|
||||
}
|
||||
|
||||
if (_state == LOCKED) {
|
||||
DEBUG("ControllerState is LOCKED, ignoring card.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_state == LOCKING) {
|
||||
_state = LOCKED;
|
||||
DEBUG("ControllerState is now LOCKED.\n");
|
||||
}
|
||||
|
||||
player->play(pl);
|
||||
//send_playlist_manager_status();
|
||||
send_controller_status();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String Controller::_read_rfid_data() {
|
||||
TRACE("_read_rfid_data() running...\n");
|
||||
static MFRC522::MIFARE_Key keys[8] = {
|
||||
{{0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7}}, // D3 F7 D3 F7 D3 F7
|
||||
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, // FF FF FF FF FF FF = factory default
|
||||
{{0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5}}, // A0 A1 A2 A3 A4 A5
|
||||
{{0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5}}, // B0 B1 B2 B3 B4 B5
|
||||
{{0x4d, 0x3a, 0x99, 0xc3, 0x51, 0xdd}}, // 4D 3A 99 C3 51 DD
|
||||
{{0x1a, 0x98, 0x2c, 0x7e, 0x45, 0x9a}}, // 1A 98 2C 7E 45 9A
|
||||
{{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}}, // AA BB CC DD EE FF
|
||||
{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}} // 00 00 00 00 00 00
|
||||
};
|
||||
SPIMaster::select_rc522();
|
||||
DEBUG("Trying to read RFID data...\n");
|
||||
|
||||
String data = "";
|
||||
MFRC522::PICC_Type type = _rfid->PICC_GetType(_rfid->uid.sak);
|
||||
|
||||
uint8_t sectors = 0;
|
||||
switch(type) {
|
||||
case MFRC522::PICC_TYPE_MIFARE_MINI: sectors = 5; break;
|
||||
case MFRC522::PICC_TYPE_MIFARE_1K: sectors = 16; break;
|
||||
case MFRC522::PICC_TYPE_MIFARE_4K: sectors = 40; break;
|
||||
default: INFO("Unknown PICC type %s\n", String(MFRC522::PICC_GetTypeName(type)).c_str());
|
||||
}
|
||||
sectors = 2; // Pretend we have only two sectors, so we read only sector #1.
|
||||
int good_key_index = -1;
|
||||
for (uint8_t sector=1; sector<sectors; sector++) {
|
||||
uint8_t blocks = (sector < 32) ? 4 : 16;
|
||||
uint8_t block_offset = (sector < 32) ? sector * 4 : 128 + (sector - 32) * 16;
|
||||
|
||||
MFRC522::StatusCode status;
|
||||
|
||||
for (int i=0; i<8; i++) {
|
||||
MFRC522::MIFARE_Key *k = &keys[i];
|
||||
TRACE("Trying MIFARE key %02X %02X %02X %02X %02X %02X...\n", k->keyByte[0], k->keyByte[1], k->keyByte[2], k->keyByte[3], k->keyByte[4], k->keyByte[5]);
|
||||
status = _rfid->PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block_offset, k, &_rfid->uid);
|
||||
if (status == MFRC522::STATUS_OK) {
|
||||
TRACE("Authentication succeeded with key #%d\n", i);
|
||||
good_key_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (good_key_index == -1) {
|
||||
TRACE("Could not find a valid MIFARE key.\n");
|
||||
} else {
|
||||
for (uint8_t block=0; block<blocks-1; block++) {
|
||||
byte buffer[18];
|
||||
uint8_t byte_count = 18;
|
||||
status = _rfid->MIFARE_Read(block_offset + block, buffer, &byte_count);
|
||||
if (status != MFRC522::STATUS_OK) {
|
||||
DEBUG("MIFARE_Read() failed: %s\n", String(_rfid->GetStatusCodeName(status)).c_str());
|
||||
continue;
|
||||
}
|
||||
for (int i=0; i<16; i++) {
|
||||
if (buffer[i]>=0x20 && buffer[i]<0x7F) data.concat((char)buffer[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_rfid->PICC_HaltA();
|
||||
_rfid->PCD_StopCrypto1();
|
||||
DEBUG("Data from RFID: %s\n", data.c_str());
|
||||
SPIMaster::select_rc522(false);
|
||||
return data;
|
||||
}
|
||||
|
||||
void Controller::_check_serial() {
|
||||
if (Serial.available() > 0) {
|
||||
char c = Serial.read();
|
||||
Serial.printf("%c", c);
|
||||
if (c==10 || c==13) {
|
||||
if (_serial_buffer.length()>0) {
|
||||
process_message(_serial_buffer);
|
||||
_serial_buffer = String();
|
||||
}
|
||||
} else {
|
||||
_serial_buffer.concat(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Controller::process_message(String cmd) {
|
||||
DEBUG("Executing command: %s\n", cmd.c_str());
|
||||
|
||||
if (cmd.startsWith("play ")) {
|
||||
Playlist* p = pm->get_playlist_for_folder(cmd.substring(5));
|
||||
player->play(p);
|
||||
} else if (cmd.equals("play")) {
|
||||
player->play();
|
||||
} else if (cmd.equals("stop")) {
|
||||
player->stop();
|
||||
} else if (cmd.equals("help")) {
|
||||
_execute_command_help();
|
||||
} else if (cmd.equals("-")) {
|
||||
player->vol_down();
|
||||
} else if (cmd.equals("+")) {
|
||||
player->vol_up();
|
||||
} else if (cmd.startsWith("volume=")) {
|
||||
uint8_t vol = cmd.substring(7).toInt();
|
||||
player->set_volume(vol);
|
||||
} else if (cmd.equals("track_prev")) {
|
||||
player->track_prev();
|
||||
} else if (cmd.equals("track_next")) {
|
||||
player->track_next();
|
||||
} else if (cmd.startsWith("track=")) {
|
||||
uint8_t track = cmd.substring(6).toInt();
|
||||
player->set_track(track);
|
||||
} else if (cmd.equals("ids")) {
|
||||
pm->dump_ids();
|
||||
} else if (cmd.equals("reset_vs1053")) {
|
||||
player->stop();
|
||||
player->init();
|
||||
} else if (cmd.equals("reboot")) {
|
||||
ESP.restart();
|
||||
} else if (cmd.startsWith("add_mapping=")) {
|
||||
String rest = cmd.substring(12);
|
||||
uint8_t idx = rest.indexOf('=');
|
||||
String id = rest.substring(0, idx);
|
||||
String folder = rest.substring(idx + 1);
|
||||
pm->add_mapping(id, folder);
|
||||
send_playlist_manager_status();
|
||||
#ifdef OTA_UPDATE_URL
|
||||
} else if (cmd.equals("update")) {
|
||||
Updater::run();
|
||||
#endif
|
||||
} else if (cmd.startsWith("trace=")) {
|
||||
int val = cmd.substring(6).toInt();
|
||||
if (val==0) {
|
||||
trace_enabled = false;
|
||||
prefs.putBool("trace_enabled", false);
|
||||
} else if (val==1) {
|
||||
trace_enabled = true;
|
||||
prefs.putBool("trace_enabled", true);
|
||||
}
|
||||
} else if (cmd.startsWith("debug=")) {
|
||||
int val = cmd.substring(6).toInt();
|
||||
if (val==0) {
|
||||
debug_enabled = false;
|
||||
prefs.putBool("debug_enabled", false);
|
||||
} else if (val==1) {
|
||||
debug_enabled = true;
|
||||
prefs.putBool("debug_enabled", true);
|
||||
}
|
||||
} else {
|
||||
ERROR("Unknown command: %s\n", cmd.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Controller::_execute_command_ls(String path) {
|
||||
INFO("Listing contents of %s:\n", path.c_str());
|
||||
// TODO
|
||||
//std::list<String> files = player->ls(path);
|
||||
//for(std::list<String>::iterator it=files.begin(); it!=files.end(); ++it) {
|
||||
// INFO(" %s\n", (*it).c_str());
|
||||
//}
|
||||
}
|
||||
|
||||
void Controller::_execute_command_help() {
|
||||
INFO("Valid commands are:");
|
||||
INFO(" help - Displays this help\n");
|
||||
//INFO(" ls [dir] - Lists the contents of [dir] or, if not given, of /\n");
|
||||
INFO(" ids - Lists all known ID-to-folder mappings\n");
|
||||
INFO(" play [id] - Plays the album with the given id\n");
|
||||
INFO(" stop - Stops playback\n");
|
||||
INFO(" - / + - Decrease or increase the volume\n");
|
||||
INFO(" p / n - Previous or next track\n");
|
||||
}
|
||||
|
||||
void Controller::_check_buttons() {
|
||||
if (BTN_PREV() && _debounce_button(0)) {
|
||||
if (_state == NORMAL) {
|
||||
player->track_prev();
|
||||
} else {
|
||||
DEBUG("Ignoring btn_prev because state is LOCKED.\n");
|
||||
}
|
||||
} else if (BTN_VOL_UP() && _debounce_button(1)) {
|
||||
player->vol_up();
|
||||
} else if (BTN_VOL_DOWN() && _debounce_button(2)) {
|
||||
player->vol_down();
|
||||
} else if (BTN_NEXT() && _debounce_button(3)) {
|
||||
if (_state == NORMAL) {
|
||||
player->track_next();
|
||||
} else {
|
||||
DEBUG("Ignoring btn_next because state is LOCKED.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Controller::_debounce_button(uint8_t index) {
|
||||
bool ret = false;
|
||||
if (_button_last_pressed_at[index] + DEBOUNCE_MILLIS < millis()) {
|
||||
DEBUG("Button %d pressed.\n", index);
|
||||
ret = true;
|
||||
}
|
||||
_button_last_pressed_at[index] = millis();
|
||||
return ret;
|
||||
}
|
||||
|
||||
String Controller::json() {
|
||||
DynamicJsonDocument json(1024);
|
||||
json["_type"] = "controller";
|
||||
switch(_state) {
|
||||
case LOCKED: json["state"] = "locked"; break;
|
||||
case LOCKING: json["state"] = "locking"; break;
|
||||
case NORMAL: json["state"] = "normal"; break;
|
||||
}
|
||||
json["is_rfid_present"] = _rfid_present;
|
||||
JsonObject rfid = json.createNestedObject("last_rfid");
|
||||
rfid["uid"] = _last_rfid_uid;
|
||||
rfid["data"] = _last_rfid_data;
|
||||
json["uptime"] = millis() / 1000;
|
||||
json["free_heap"] = ESP.getFreeHeap();
|
||||
JsonObject versions = json.createNestedObject("versions");
|
||||
versions["ota"] = OTA_VERSION;
|
||||
#ifdef VERSION
|
||||
versions["release"] = VERSION;
|
||||
#else
|
||||
versions["release"] = "unknown";
|
||||
#endif
|
||||
|
||||
JsonObject wifi = json.createNestedObject("wifi");
|
||||
if (WiFi.isConnected()) {
|
||||
wifi["connected"] = true;
|
||||
wifi["ssid"] = WiFi.SSID();
|
||||
wifi["rssi"] = WiFi.RSSI();
|
||||
} else {
|
||||
wifi["connected"] = false;
|
||||
}
|
||||
return json.as<String>();
|
||||
}
|
||||
|
||||
void Controller::send_player_status() {
|
||||
TRACE("In send_player_status()...\n");
|
||||
|
||||
if (_http_server->ws->count() > 0) {
|
||||
_http_server->ws->textAll(player->json());
|
||||
_http_server->ws->textAll(player->position_json());
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::send_playlist_manager_status() {
|
||||
TRACE("In send_playlist_manager_status()...\n");
|
||||
if (_http_server->ws->count() > 0) {
|
||||
_http_server->ws->textAll(pm->json());
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::send_position() {
|
||||
TRACE("In send_position()...\n");
|
||||
if (_http_server->ws->count() > 0) {
|
||||
_http_server->ws->textAll(player->position_json());
|
||||
}
|
||||
_last_position_info_at = millis();
|
||||
}
|
||||
|
||||
void Controller::send_controller_status() {
|
||||
TRACE("In send_controller_status()...\n");
|
||||
if (_http_server->ws->count() > 0) {
|
||||
_http_server->ws->textAll(json());
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::inform_new_client(AsyncWebSocketClient* client) {
|
||||
String s;
|
||||
s += pm->json();
|
||||
s += '\n';
|
||||
s += player->json();
|
||||
s += '\n';
|
||||
s += player->position_json();
|
||||
s += '\n';
|
||||
s += json();
|
||||
client->text(s);
|
||||
}
|
||||
|
||||
void Controller::queue_command(String s) {
|
||||
DEBUG("Enqeueing command '%s'.\n", s.c_str());
|
||||
_cmd_queue = s;
|
||||
}
|
||||
|
||||
void Controller::update_playlist_manager() {
|
||||
pm->scan_files();
|
||||
send_playlist_manager_status();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user