RfidShelf.ino 17.9 KB
Newer Older
Nitek's avatar
Nitek committed
1 2 3 4
#include <ESP8266WiFi.h>
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>
5 6 7
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
Nitek's avatar
Nitek committed
8 9
#include <SPI.h>
#include <MFRC522.h>
10
#include <SdFat.h>
11
#include <Adafruit_VS1053.h>
Nitek's avatar
Nitek committed
12

13 14 15 16 17
// Legacy wiring
//#define RC522_CS        D3
//#define BREAKOUT_CS     D4     // VS1053 chip select pin (output)
//#define BREAKOUT_RESET  D8     // VS1053 reset pin (output)
//#define AMP_POWER       -1
Nitek's avatar
Nitek committed
18

19 20 21 22 23
#define RC522_CS        D8
#define SD_CS           D2

#define BREAKOUT_RESET  -1     // VS1053 reset pin (output)
#define BREAKOUT_CS     D3     // VS1053 chip select pin (output)
24
#define BREAKOUT_DCS    D0    // VS1053 Data/command select pin (output)
25 26 27
#define DREQ            D1    // VS1053 Data request (input)

#define AMP_POWER       D4
Nitek's avatar
Nitek committed
28 29

Adafruit_VS1053_FilePlayer musicPlayer =
30
  Adafruit_VS1053_FilePlayer(BREAKOUT_RESET, BREAKOUT_CS, BREAKOUT_DCS, DREQ, SD_CS);
Nitek's avatar
Nitek committed
31 32

WiFiManager wifiManager;
Nitek's avatar
Nitek committed
33

Nitek's avatar
Nitek committed
34
ESP8266WebServer server(80);
Nitek's avatar
Nitek committed
35

36
MFRC522 mfrc522(RC522_CS, UINT8_MAX);   // Create MFRC522 instance.
Nitek's avatar
Nitek committed
37 38 39

MFRC522::MIFARE_Key key;

40

41 42 43 44 45
// Init array that will store new card uid
byte lastCardUid[4];
bool playing = false;
bool pairing = false;
String currentFile = "";
Nitek's avatar
Nitek committed
46

47 48
SdFat SD;
SdFile uploadFile;
Nitek's avatar
Nitek committed
49 50 51

void setup() {
  Serial.begin(115200);
52
  Serial.println();
53 54 55 56 57 58 59 60 61 62
  Serial.println("Starting ...");

  // Seems to make flashing more reliable
  delay(100);

  if(AMP_POWER > 0) {
    // Disable amp
    pinMode(AMP_POWER, OUTPUT);
    digitalWrite(AMP_POWER, LOW);
  }
63 64

  // Init SPI SS pins
65 66 67 68
  pinMode(RC522_CS, OUTPUT);
  digitalWrite(RC522_CS, HIGH);
  pinMode(SD_CS, OUTPUT);
  digitalWrite(SD_CS, HIGH);
69 70 71 72
  pinMode(BREAKOUT_CS, OUTPUT);
  digitalWrite(BREAKOUT_CS, HIGH);
  pinMode(BREAKOUT_DCS, OUTPUT);
  digitalWrite(BREAKOUT_DCS, HIGH);
73
  
Nitek's avatar
Nitek committed
74 75 76 77 78 79 80 81
  SPI.begin();        // Init SPI bus
  mfrc522.PCD_Init(); // Init MFRC522 card

  for (byte i = 0; i < 6; i++) {
    key.keyByte[i] = 0xFF;
  }

  Serial.print(F("Using rfid key (for A and B):"));
82
  print_byte_array(key.keyByte, MFRC522::MF_KEY_SIZE);
Nitek's avatar
Nitek committed
83
  Serial.println();
Nitek's avatar
Nitek committed
84 85

  //Initialize the SdCard.
86
  if (!SD.begin(SD_CS)) SD.initErrorHalt();
Nitek's avatar
Nitek committed
87 88 89 90

  // initialise the music player
  if (! musicPlayer.begin()) { // initialise the music player
    Serial.println(F("Couldn't find VS1053, do you have the right pins defined?"));
91
    while (1) delay(500);
Nitek's avatar
Nitek committed
92 93 94
  }
  Serial.println(F("VS1053 found"));

95 96 97 98
  /* Fix for the design fuckup of the cheap LC Technology MP3 shield
    see http://www.bajdi.com/lcsoft-vs1053-mp3-module/#comment-33773
    Doesn't hurt for other shields
  */
Nitek's avatar
Nitek committed
99 100 101 102 103
  musicPlayer.sciWrite(VS1053_REG_WRAMADDR, VS1053_GPIO_DDR);
  musicPlayer.sciWrite(VS1053_REG_WRAM, 0x0003);
  musicPlayer.GPIO_digitalWrite(0x0000);
  musicPlayer.softReset();

104 105 106 107 108 109 110 111 112 113 114 115
  if (patchVS1053()) {
    // Enable Mono Output
    musicPlayer.sciWrite(VS1053_REG_WRAMADDR, 0x1e09);
    musicPlayer.sciWrite(VS1053_REG_WRAM, 0x0001);

    // Enable differential output
    /*uint16_t mode = VS1053_MODE_SM_DIFF | VS1053_MODE_SM_SDINEW;
      musicPlayer.sciWrite(VS1053_REG_MODE, mode); */
  } else {
    Serial.println("Could not load patch");
  }

Nitek's avatar
Nitek committed
116 117 118
  Serial.print(F("SampleRate "));
  Serial.println(musicPlayer.sciRead(VS1053_REG_AUDATA));

119 120 121
  musicPlayer.setVolume(10, 10);

  musicPlayer.dumpRegs();
Nitek's avatar
Nitek committed
122

123 124
  wifiManager.autoConnect("MP3-SHELF", "lacklack");

Nitek's avatar
Nitek committed
125 126 127 128 129
  Serial.print("Connected! IP address: ");
  Serial.println(WiFi.localIP());

  server.on("/playMp3", HTTP_GET, handlePlayMp3);
  server.on("/stopMp3", HTTP_GET, handleStopMp3);
130
  server.on("/", HTTP_POST, handleNotFound, handleFileUpload);
Nitek's avatar
Nitek committed
131 132 133 134
  server.onNotFound(handleNotFound);

  server.begin();

135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
  ArduinoOTA.onStart([]() {
    Serial.println("Start");
  });
  ArduinoOTA.onEnd([]() {
    Serial.println("\nEnd");
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  });
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
    else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
    else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
    else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
    else if (error == OTA_END_ERROR) Serial.println("End Failed");
  });
  ArduinoOTA.begin();

Nitek's avatar
Nitek committed
154
  Serial.println(F("Init done"));
Nitek's avatar
Nitek committed
155 156 157
}

void loop() {
158 159

  if (playing && musicPlayer.playingMusic) {
Nitek's avatar
Nitek committed
160
    musicPlayer.feedBuffer();
161
  } else if (playing) {
Nitek's avatar
Nitek committed
162 163
    playMp3();
  }
Nitek's avatar
Nitek committed
164

165
  handleRfid();
Nitek's avatar
Nitek committed
166
  server.handleClient();
167
  ArduinoOTA.handle();
Nitek's avatar
Nitek committed
168 169 170
}

void handleRfid() {
171
  // Look for new cards
Nitek's avatar
Nitek committed
172
  if ( ! mfrc522.PICC_IsNewCardPresent()) {
Nitek's avatar
Nitek committed
173
    return;
Nitek's avatar
Nitek committed
174
  }
Nitek's avatar
Nitek committed
175 176

  // Select one of the cards
Nitek's avatar
Nitek committed
177
  if ( ! mfrc522.PICC_ReadCardSerial()) {
Nitek's avatar
Nitek committed
178
    return;
Nitek's avatar
Nitek committed
179 180
  }

181 182 183 184 185
  if (!pairing && (
        mfrc522.uid.uidByte[0] == lastCardUid[0] &&
        mfrc522.uid.uidByte[1] == lastCardUid[1] &&
        mfrc522.uid.uidByte[2] == lastCardUid[2] &&
        mfrc522.uid.uidByte[3] == lastCardUid[3] )) {
Nitek's avatar
Nitek committed
186 187 188 189
    mfrc522.PICC_HaltA();
    Serial.println(F("Card already read"));
    return;
  }
Nitek's avatar
Nitek committed
190 191 192

  // Show some details of the PICC (that is: the tag/card)
  Serial.print(F("Card UID:"));
193
  print_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size);
Nitek's avatar
Nitek committed
194 195 196 197 198 199 200 201 202 203 204
  Serial.println();
  Serial.print(F("PICC type: "));
  MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak);
  Serial.println(mfrc522.PICC_GetTypeName(piccType));

  // Check for compatibility
  if ( piccType != MFRC522::PICC_TYPE_MIFARE_1K ) {
    Serial.println(F("Unsupported card."));
    return;
  }

205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
  if (pairing) {
    char writeFolder[17];
    SD.vwd()->getName(writeFolder, 17);
    writeRfidBlock(1, 0, writeFolder);
    pairing = false;
  }

  byte readBuffer[18];
  if (readRfidBlock(1, 0, readBuffer,sizeof(readBuffer))) {
    
    char readFolder[18];
    readFolder[0] = '/';
    // allthough sizeof(readBuffer) == 18, we only get 16 byte of data
    for (byte i = 0; i < 16; i++) {
      readFolder[i+1] = readBuffer[i];
    }
    // readBuffer will already contain \0 if the folder name is < 16 chars, but otherwise we need to add it
    readFolder[17] = '\0';
Nitek's avatar
Nitek committed
223

Nitek's avatar
Nitek committed
224 225
    // Store NUID into nuidPICC array
    for (byte i = 0; i < 4; i++) {
226
      lastCardUid[i] = mfrc522.uid.uidByte[i];
Nitek's avatar
Nitek committed
227
    }
Nitek's avatar
Nitek committed
228

229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
    if (switchFolder(readFolder)) {
      stopMp3();
      yield();
      playMp3();
    }
  }

  // Halt PICC
  mfrc522.PICC_HaltA();
  // Stop encryption on PCD
  mfrc522.PCD_StopCrypto1();

}

bool writeRfidBlock(uint8_t sector, uint8_t relativeBlock, char *newContent) {
  uint8_t absoluteBlock = (sector * 4) + relativeBlock;
  MFRC522::StatusCode status;


  // Authenticate using key A
  Serial.println(F("Authenticating using key A..."));
  status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, absoluteBlock, &key, &(mfrc522.uid));
  if (status != MFRC522::STATUS_OK) {
    Serial.print(F("PCD_Authenticate() failed: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
    return false;
Nitek's avatar
Nitek committed
255
  }
Nitek's avatar
Nitek committed
256

257 258 259 260 261 262 263 264 265 266 267 268 269 270
  byte buffer[16];
  for (byte i = 0; i < 16; i++) {
    buffer[i] = newContent[i];
  }

  // Write block
  Serial.print(F("Writing data to block: ")); Serial.println(newContent);
  status = (MFRC522::StatusCode) mfrc522.MIFARE_Write(absoluteBlock, buffer, 16);
  if (status != MFRC522::STATUS_OK) {
    Serial.print(F("MIFARE_Write() failed: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
    return false;
  }
  return true;
Nitek's avatar
Nitek committed
271 272
}

273 274 275 276
/**
 * read a block from a rfid card into outputBuffer which needs to be >= 18 bytes long
 */
bool readRfidBlock(uint8_t sector, uint8_t relativeBlock, byte *outputBuffer, byte bufferSize) {
Nitek's avatar
Nitek committed
277 278
  if (relativeBlock > 3) {
    Serial.println(F("Invalid block number"));
Nitek's avatar
Nitek committed
279
    return false;
Nitek's avatar
Nitek committed
280 281 282
  }

  // Block 4 is trailer block
283 284
  //uint8_t trailerBlock   = (sector * 4) + 3;
  uint8_t absoluteBlock = (sector * 4) + relativeBlock;
Nitek's avatar
Nitek committed
285 286 287 288
  MFRC522::StatusCode status;

  // Authenticate using key A
  Serial.println(F("Authenticating using key A..."));
289
  status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, absoluteBlock, &key, &(mfrc522.uid));
Nitek's avatar
Nitek committed
290 291 292
  if (status != MFRC522::STATUS_OK) {
    Serial.print(F("PCD_Authenticate() failed: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
Nitek's avatar
Nitek committed
293
    return false;
Nitek's avatar
Nitek committed
294 295
  }

Nitek's avatar
Nitek committed
296 297
  Serial.print(F("Reading block "));
  Serial.println(absoluteBlock);
298
  status = (MFRC522::StatusCode) mfrc522.MIFARE_Read(absoluteBlock, outputBuffer, &bufferSize);
Nitek's avatar
Nitek committed
299 300 301
  if (status != MFRC522::STATUS_OK) {
    Serial.print(F("MIFARE_Read() failed: "));
    Serial.println(mfrc522.GetStatusCodeName(status));
Nitek's avatar
Nitek committed
302
    return false;
Nitek's avatar
Nitek committed
303 304
  }
  Serial.print(F("Data in block ")); Serial.print(absoluteBlock); Serial.println(F(":"));
305
  print_byte_array(outputBuffer, 16);
Nitek's avatar
Nitek committed
306 307 308
  Serial.println();
  Serial.println();

Nitek's avatar
Nitek committed
309
  return true;
Nitek's avatar
Nitek committed
310 311
}

312 313

void print_byte_array(byte *buffer, byte bufferSize) {
Nitek's avatar
Nitek committed
314 315 316 317 318 319
  for (byte i = 0; i < bufferSize; i++) {
    Serial.print(buffer[i] < 0x10 ? " 0" : " ");
    Serial.print(buffer[i], HEX);
  }
}

320
bool switchFolder(char *folder) {
Nitek's avatar
Nitek committed
321 322
  Serial.print(F("Switching folder to "));
  Serial.println(folder);
323 324 325

  if (!SD.exists(folder)) {
    Serial.println(F("Folder does not exist"));
326
    return false;
327 328 329
  }
  SD.chdir(folder);
  SD.vwd()->rewind();
330 331
  currentFile = "";
  return true;
Nitek's avatar
Nitek committed
332 333 334
}

void stopMp3() {
335
  playing = false;
336 337 338
  if(AMP_POWER > 0) {
    digitalWrite(AMP_POWER, LOW);
  }
339
  musicPlayer.stopPlaying();
Nitek's avatar
Nitek committed
340 341 342
}

void playMp3() {
343
  SdFile file;
344 345 346 347 348 349 350 351
  SD.vwd()->rewind();

  char filenameChar[100];
  SD.vwd()->getName(filenameChar, 100);
  String dirname = "/" + String(filenameChar) + "/";

  String nextFile = "";

352
  while (file.openNext(SD.vwd(), O_READ))
Nitek's avatar
Nitek committed
353
  {
354 355

    file.getName(filenameChar, 100);
Nitek's avatar
Nitek committed
356 357
    file.close();

358 359
    String tmpFile = String(filenameChar);
    if (file.isDir() && !isMP3File(tmpFile)) {
Nitek's avatar
Nitek committed
360
      Serial.print(F("Ignoring "));
361 362 363 364 365 366
      Serial.println(tmpFile);
      continue;
    }

    if(currentFile < tmpFile && (tmpFile < nextFile || nextFile == "")) {
      nextFile = tmpFile;
Nitek's avatar
Nitek committed
367
    }
Nitek's avatar
Nitek committed
368
  }
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395

  // Start folder from the beginning
  if(nextFile == "" && currentFile != ""){
    currentFile = "";
    playMp3();
    return;
  }

  // No currentFile && no nextFile => Nothing to play!
  if(nextFile == "") {
    Serial.print(F("No mp3 files in "));
    Serial.println(dirname);
    stopMp3();
    return;
  }

  String fullpath = dirname + nextFile;

  Serial.print(F("Playing "));
  Serial.println(fullpath);

  playing = true;
  musicPlayer.startPlayingFile((char *)fullpath.c_str());

  if(AMP_POWER > 0) {
    digitalWrite(AMP_POWER, HIGH);
  }
Nitek's avatar
Nitek committed
396 397
}

398
bool isMP3File(String &filename) {
399
  return filename.endsWith(".mp3");
Nitek's avatar
Nitek committed
400 401 402 403 404 405
}

void returnOK() {
  server.send(200, "text/plain", "");
}

406 407
void returnHttpStatus(uint8_t statusCode, String msg) {
  server.send(statusCode, "text/plain", msg);
Nitek's avatar
Nitek committed
408 409
}

410
void renderDirectory(String &path) {
411 412
  SdFile dir;
  dir.open(path.c_str(), O_READ);
Nitek's avatar
Nitek committed
413

414 415 416 417
  dir.rewind();
  server.setContentLength(CONTENT_LENGTH_UNKNOWN);
  server.send(200, "text/html", "");
  WiFiClient client = server.client();
Nitek's avatar
Nitek committed
418

419 420 421 422 423
  server.sendContent(
    F("<html><head><script>"
      "function deleteUrl(url){ var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if (xhr.readyState === 4) { location.reload(); }}; xhr.open('DELETE', url); xhr.send();}"
      "function upload(folder){ var fileInput = document.getElementById('fileInput'); if(fileInput.files.length === 0){ alert('Choose a file first'); return; } xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if (xhr.readyState === 4) { location.reload(); }};"
      "var formData = new FormData(); for(var i = 0; i < fileInput.files.length; i++) { formData.append('data', fileInput.files[i], folder.concat(fileInput.files[i].name)); }; xhr.open('POST', '/'); xhr.send(formData); }"
424 425
      "function writeRfid(url){ var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if (xhr.readyState === 4) { location.reload(); }}; xhr.open('POST', url); var formData = new FormData(); formData.append('write', 1); xhr.send(formData);}"
      "function mkdir() { var folder = document.getElementById('folder'); if(folder != ''){ var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if (xhr.readyState === 4) { location.reload(); }}; xhr.open('POST', '/'); var formData = new FormData(); formData.append('folder', folder.value); xhr.send(formData);}}"
426 427 428 429 430 431 432 433 434 435 436 437
      "</script></head><body>"));

  String output;
  if (path == "/") {
    output = F("<form><input type=\"text\" name=\"folder\" id=\"folder\"><input type=\"button\" value=\"mkdir\" onclick=\"mkdir()\"></form>");
    output.replace("{folder}", path);
    server.sendContent(output);
  } else {
    output = F("<form><input type=\"file\" multiple=\"true\" name=\"data\" id=\"fileInput\"><input type=\"button\" value=\"upload\" onclick=\"upload('{folder}')\"></form>");
    output.replace("{folder}", path);
    server.sendContent(output);
    server.sendContent(F("<div><a href=\"..\">..</a></div>"));
Nitek's avatar
Nitek committed
438 439
  }

440 441 442 443 444 445 446 447 448
  SdFile entry;
  while (entry.openNext(&dir, O_READ)) {
    char filenameChar[100];
    entry.getName(filenameChar, 100);
    // TODO encode special characters
    String filename = String(filenameChar);

    String output = F("<div id=\"{name}\">{icon}<a href=\"{path}\">{name}</a> <a href=\"#\" onclick=\"deleteUrl('{name}'); return false;\">&#x2718;</a>");
    if (entry.isDir()) {
449 450 451 452
      // Currently only foldernames <= 16 characters can be written onto the rfid
      if (filename.length() <= 16) {
        output += F("<a href=\"#\" onclick=\"writeRfid('{name}');\">&#x1f4be;</a>");
      }
453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
      output.replace("{icon}", F("&#x1f4c2; "));
      output.replace("{path}", filename + "/");
    } else {
      if (isMP3File(filename)) {
        output.replace("{icon}", F("&#x266b; "));
      } else {
        output.replace("{icon}", F(""));
      }
      output.replace("{path}", filename);
    }
    output += F("</div>");
    output.replace("{name}", filename);
    server.sendContent(output);
    entry.close();
  }
  server.sendContent(F("</body></html>"));
  server.client().stop();
}

472
bool loadFromSdCard(String &path) {
473
  String dataType = "text/plain";
Nitek's avatar
Nitek committed
474

475 476 477 478
  if (path.endsWith(".HTM")) dataType = "text/html";
  else if (path.endsWith(".CSS")) dataType = "text/css";
  else if (path.endsWith(".JS")) dataType = "application/javascript";
  else dataType = "application/octet-stream";
Nitek's avatar
Nitek committed
479

480 481 482 483 484
  File dataFile = SD.open(path.c_str());

  if (!dataFile) {
    Serial.println("File not open");
    return false;
Nitek's avatar
Nitek committed
485 486
  }

487 488 489 490 491 492 493 494
  if (dataFile.isDir()) {
    // dataFile.name() will always be "/" for directorys, so we cannot know if we are in the root directory without handing it over
    renderDirectory(path);
  } else {
    if (server.streamFile(dataFile, dataType) != dataFile.size()) {
      Serial.println("Sent less data than expected!");
    }
  }
Nitek's avatar
Nitek committed
495 496 497 498
  dataFile.close();
  return true;
}

499 500 501 502 503 504 505 506 507 508
void handleWriteRfid(String &folder) {
  if (switchFolder((char *)folder.c_str())) {
    stopMp3();
    pairing = true;
    returnOK();
  } else {
    returnHttpStatus((uint8_t)404, "Not found");
  }
}

Nitek's avatar
Nitek committed
509 510 511 512 513 514 515 516 517
void handlePlayMp3() {
  playMp3();
  returnOK();
}

void handleStopMp3() {
  stopMp3();
  returnOK();
}
518 519 520 521
void handleFileUpload() {
  // Upload always happens on /
  if (server.uri() != "/") {
    Serial.println(F("Invalid upload URI"));
Nitek's avatar
Nitek committed
522
    return;
Nitek's avatar
Nitek committed
523 524
  }

525
  HTTPUpload& upload = server.upload();
Nitek's avatar
Nitek committed
526

527 528
  if (upload.status == UPLOAD_FILE_START) {
    String filename = upload.filename;
Nitek's avatar
Nitek committed
529

530 531 532 533 534
    if (!isMP3File(filename)) {
      Serial.print(F("Not a MP3: "));
      Serial.println(filename);
      return;
    }
Nitek's avatar
Nitek committed
535

536 537 538 539
    if (!filename.startsWith("/")) {
      Serial.println(F("Invalid upload target"));
      return;
    }
Nitek's avatar
Nitek committed
540

541 542 543
    if (SD.exists((char *)filename.c_str())) {
      Serial.println("File " + filename + " already exists. Skipping");
      return;
Nitek's avatar
Nitek committed
544 545
    }

546 547 548 549 550 551 552 553 554 555 556 557 558 559 560
    uploadFile.open((char *)filename.c_str(), FILE_WRITE);
    Serial.print(F("UPLOAD_FILE_START: "));
    Serial.println(filename);
  } else if (upload.status == UPLOAD_FILE_WRITE) {
    if (uploadFile.isOpen()) {
      uploadFile.write(upload.buf, upload.currentSize);
      Serial.print(F("UPLOAD_FILE_WRITE: "));
      Serial.println(upload.currentSize);
    }
  } else if (upload.status == UPLOAD_FILE_END) {
    if (uploadFile.isOpen()) {
      uploadFile.close();
      Serial.print(F("UPLOAD_FILE_END: "));
      Serial.println(upload.totalSize);
    }
Nitek's avatar
Nitek committed
561 562 563
  }
}

564 565
void handleNotFound() {
  String path = server.urlDecode(server.uri());
566
  Serial.println(F("Request to: ") + path);
567 568 569 570
  if (server.method() == HTTP_GET) {
    if (loadFromSdCard(path)) return;
  } else if (server.method() == HTTP_DELETE) {
    if (server.uri() == "/" || !SD.exists((char *)path.c_str())) {
571
      returnHttpStatus((uint8_t)500, "BAD PATH: " + server.uri());
572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
      return;
    }

    SdFile file;
    file.open(path.c_str());
    if (file.isDir()) {
      file.rmRfStar();
    } else {
      file.remove();
    }
    returnOK();
    return;
  } else if (server.method() == HTTP_POST) {
    if (server.hasArg("folder")) {
      Serial.println(F("Creating folder"));
      SD.mkdir((char *)server.arg("folder").c_str());
      returnOK();
      return;
590
    } else if (server.uri() == "/") {
591 592 593
      Serial.println(F("Probably got an upload request"));
      returnOK();
      return;
594 595 596 597 598 599
    } else if (SD.exists((char *)path.c_str())) {
      if (server.hasArg("write") && path.length() <= 16) {
        handleWriteRfid(path);
        returnOK();
        return;
      }
600
    }
Nitek's avatar
Nitek committed
601
  }
602

603 604
  // 404 otherwise
  returnHttpStatus((uint8_t)404, "Not found");
605
  Serial.println("404: " + path);
Nitek's avatar
Nitek committed
606
}
607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643

bool patchVS1053() {
  uint16_t i = 0;

  Serial.println(F("Installing patch to VS1053"));

  SdFile file;
  if (!file.open("patches.053", O_READ)) return false;

  uint16_t addr, n, val;

  while (file.read(&addr, 2) && file.read(&n, 2)) {
    i += 2;
    if (n & 0x8000U) {
      n &= 0x7FFF;
      if (!file.read(&val, 2)) {
        file.close();
        return false;
      }
      while (n--) {
        musicPlayer.sciWrite(addr, val);
      }
    } else {
      while (n--) {
        if (!file.read(&val, 2)) {
          file.close();
          return false;
        }
        i++;
        musicPlayer.sciWrite(addr, val);
      }
    }
  }
  file.close(); //Close out this track

  Serial.print(F("Number of bytes: ")); Serial.println(i);
}