diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index bbd6acd..4478041 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -10,11 +10,12 @@ "/Users/fabian/Documents/PlatformIO/Projects/esmp3/include", "/Users/fabian/Documents/PlatformIO/Projects/esmp3/src", "/Users/fabian/Documents/PlatformIO/Projects/esmp3/lib/pubsubclient/src", - "/Users/fabian/Documents/PlatformIO/Projects/esmp3/lib/esp8266FTPServer", "/Users/fabian/.platformio/packages/framework-arduinoespressif32/libraries/SD/src", - "/Users/fabian/.platformio/packages/framework-arduinoespressif32/libraries/SPIFFS/src", + "/Users/fabian/Documents/PlatformIO/Projects/esmp3/.pio/libdeps/esp32/ESP Async WebServer/src", + "/Users/fabian/Documents/PlatformIO/Projects/esmp3/.pio/libdeps/esp32/ArduinoJson_ID64/src", "/Users/fabian/.platformio/packages/framework-arduinoespressif32/libraries/WiFi/src", "/Users/fabian/.platformio/packages/framework-arduinoespressif32/libraries/FS/src", + "/Users/fabian/Documents/PlatformIO/Projects/esmp3/.pio/libdeps/esp32/AsyncTCP_ID1826/src", "/Users/fabian/Documents/PlatformIO/Projects/esmp3/.pio/libdeps/esp32/MFRC522_ID63/src", "/Users/fabian/.platformio/packages/framework-arduinoespressif32/libraries/SPI/src", "/Users/fabian/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/config", @@ -75,6 +76,7 @@ "/Users/fabian/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/fb_gfx", "/Users/fabian/.platformio/packages/framework-arduinoespressif32/cores/esp32", "/Users/fabian/.platformio/packages/framework-arduinoespressif32/variants/esp32", + "/Users/fabian/Documents/PlatformIO/Projects/esmp3/.pio/libdeps/esp32/ESP Async WebServer_ID306/src", "/Users/fabian/.platformio/lib/Adafruit MCP23017 Arduino Library_ID334", "/Users/fabian/.platformio/lib/FastLED_ID126", "/Users/fabian/.platformio/lib/PubSubClient_ID89/src", @@ -93,6 +95,7 @@ "/Users/fabian/.platformio/packages/framework-arduinoespressif32/libraries/NetBIOS/src", "/Users/fabian/.platformio/packages/framework-arduinoespressif32/libraries/Preferences/src", "/Users/fabian/.platformio/packages/framework-arduinoespressif32/libraries/SD_MMC/src", + "/Users/fabian/.platformio/packages/framework-arduinoespressif32/libraries/SPIFFS/src", "/Users/fabian/.platformio/packages/framework-arduinoespressif32/libraries/SimpleBLE/src", "/Users/fabian/.platformio/packages/framework-arduinoespressif32/libraries/Ticker/src", "/Users/fabian/.platformio/packages/framework-arduinoespressif32/libraries/Update/src", @@ -108,11 +111,12 @@ "/Users/fabian/Documents/PlatformIO/Projects/esmp3/include", "/Users/fabian/Documents/PlatformIO/Projects/esmp3/src", "/Users/fabian/Documents/PlatformIO/Projects/esmp3/lib/pubsubclient/src", - "/Users/fabian/Documents/PlatformIO/Projects/esmp3/lib/esp8266FTPServer", "/Users/fabian/.platformio/packages/framework-arduinoespressif32/libraries/SD/src", - "/Users/fabian/.platformio/packages/framework-arduinoespressif32/libraries/SPIFFS/src", + "/Users/fabian/Documents/PlatformIO/Projects/esmp3/.pio/libdeps/esp32/ESP Async WebServer/src", + "/Users/fabian/Documents/PlatformIO/Projects/esmp3/.pio/libdeps/esp32/ArduinoJson_ID64/src", "/Users/fabian/.platformio/packages/framework-arduinoespressif32/libraries/WiFi/src", "/Users/fabian/.platformio/packages/framework-arduinoespressif32/libraries/FS/src", + "/Users/fabian/Documents/PlatformIO/Projects/esmp3/.pio/libdeps/esp32/AsyncTCP_ID1826/src", "/Users/fabian/Documents/PlatformIO/Projects/esmp3/.pio/libdeps/esp32/MFRC522_ID63/src", "/Users/fabian/.platformio/packages/framework-arduinoespressif32/libraries/SPI/src", "/Users/fabian/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/config", @@ -173,6 +177,7 @@ "/Users/fabian/.platformio/packages/framework-arduinoespressif32/tools/sdk/include/fb_gfx", "/Users/fabian/.platformio/packages/framework-arduinoespressif32/cores/esp32", "/Users/fabian/.platformio/packages/framework-arduinoespressif32/variants/esp32", + "/Users/fabian/Documents/PlatformIO/Projects/esmp3/.pio/libdeps/esp32/ESP Async WebServer_ID306/src", "/Users/fabian/.platformio/lib/Adafruit MCP23017 Arduino Library_ID334", "/Users/fabian/.platformio/lib/FastLED_ID126", "/Users/fabian/.platformio/lib/PubSubClient_ID89/src", @@ -191,6 +196,7 @@ "/Users/fabian/.platformio/packages/framework-arduinoespressif32/libraries/NetBIOS/src", "/Users/fabian/.platformio/packages/framework-arduinoespressif32/libraries/Preferences/src", "/Users/fabian/.platformio/packages/framework-arduinoespressif32/libraries/SD_MMC/src", + "/Users/fabian/.platformio/packages/framework-arduinoespressif32/libraries/SPIFFS/src", "/Users/fabian/.platformio/packages/framework-arduinoespressif32/libraries/SimpleBLE/src", "/Users/fabian/.platformio/packages/framework-arduinoespressif32/libraries/Ticker/src", "/Users/fabian/.platformio/packages/framework-arduinoespressif32/libraries/Update/src", @@ -204,7 +210,7 @@ "defines": [ "PLATFORMIO=40100", "ARDUINO_ESP32_DEV", - "VERSION=\"0.1-12-ged788a9-dirty\"", + "VERSION=\"0.1-24-g0cddfaf-dirty-20191116-131638\"", "ESP32", "ESP_PLATFORM", "F_CPU=240000000L", diff --git a/include/config.h b/include/config.h index a15af31..0030827 100644 --- a/include/config.h +++ b/include/config.h @@ -2,14 +2,16 @@ #include #define SHOW_DEBUG -#define SHOW_TRACE +//#define SHOW_TRACE #define FTP_DEBUG +#define DELAY_AFTER_DEBUG_AND_TRACE 0 #define WIFI_SSID "Schlenz" #define WIFI_PASS "1410WischlingenPanda" #define VS1053_SLEEP_DELAY 5000 #define MQTT_REPORT_INTERVAL 10000 +#define POSITION_SEND_INTERVAL 5000 #define PIN_SD_CS(x) (digitalWrite(16, x)) #define PIN_SD_CS_SETUP() (pinMode(16, OUTPUT)) @@ -68,13 +70,13 @@ #define ERROR(x, ...) Serial.printf(x, ##__VA_ARGS__) #ifdef SHOW_DEBUG - #define DEBUG(x, ...) Serial.printf(x, ##__VA_ARGS__) + #define DEBUG(x, ...) {Serial.printf(x, ##__VA_ARGS__); delay(DELAY_AFTER_DEBUG_AND_TRACE);} #else #define DEBUG(x, ...) while(0) {} #endif #ifdef SHOW_TRACE - #define TRACE(x, ...) Serial.printf(x, ##__VA_ARGS__) + #define TRACE(x, ...) {Serial.printf(x, ##__VA_ARGS__); delay(DELAY_AFTER_DEBUG_AND_TRACE);} #else #define TRACE(x, ...) while(0) {} #endif diff --git a/include/controller.h b/include/controller.h index a568e62..4131a3d 100644 --- a/include/controller.h +++ b/include/controller.h @@ -1,11 +1,16 @@ #pragma once #include +#include #include "config.h" + +class Controller; + #include "player.h" #include "playlist.h" #include "playlist_manager.h" #include "mqtt_client.h" +#include "http_server.h" #include enum ControllerState { NORMAL, LOCKING, LOCKED }; @@ -14,6 +19,7 @@ class Controller { private: MFRC522* _rfid; MQTTClient* _mqtt_client; + HTTPServer* _http_server; PlaylistManager* _pm; ControllerState _state = NORMAL; bool _rfid_enabled = true; @@ -24,12 +30,13 @@ private: uint32_t _get_rfid_card_uid(); String _read_rfid_data(); bool _rfid_present = false; - uint32_t _last_rfid_card_uid = 0; - uint8_t _no_rfid_card_count = 0; + String _last_rfid_uid = ""; + String _last_rfid_data = ""; Player* _player; unsigned long _last_rfid_scan_at = 0; + unsigned long _last_position_info_at = 0; String _serial_buffer = String(); - void _execute_serial_command(String cmd); + String _cmd_queue = ""; void _execute_command_ls(String path); void _execute_command_ids(); void _execute_command_help(); @@ -40,6 +47,13 @@ private: public: Controller(Player* p, PlaylistManager* pm); void set_mqtt_client(MQTTClient* m); - String get_status_json(); + void register_http_server(HTTPServer* h); void loop(); + void send_player_status(); + void send_playlist_manager_status(); + void send_position(); + void inform_new_client(AsyncWebSocketClient* client); + String json(); + bool process_message(String m); + void queue_command(String cmd); }; diff --git a/include/http_server.h b/include/http_server.h index a94c928..feefc31 100644 --- a/include/http_server.h +++ b/include/http_server.h @@ -1,29 +1,28 @@ -/* #pragma once -#include + +class HTTPServer; + #include "player.h" #include "controller.h" -#include -#include -#include -#include +#include +#include class HTTPServer { private: - ESP8266WebServer* _http_server; - void _handle_upload(); + AsyncWebServer* _server; + + Player* _player; + Controller* _controller; + void _handle_upload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final); uint16_t _chunk_length; uint8_t* _chunk; uint32_t _file_size; uint32_t _file_size_done; bool _need_header; uint32_t _upload_position; - void _handle_index(); - void _handle_status(); - Player* _player; - Controller* _controller; + void _onEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len); + void _handle_index(AsyncWebServerRequest* req); public: HTTPServer(Player* p, Controller* c); - void loop(); + AsyncWebSocket* ws; }; -*/ \ No newline at end of file diff --git a/include/index.html b/include/index.html new file mode 100644 index 0000000..e93957c --- /dev/null +++ b/include/index.html @@ -0,0 +1,236 @@ +const char* html = R"V0G0N( + + + + + ESMP3 + + + + + + + + +
+
+
+

+
+
+

+

+
+
+
+
+
+
+ +
+
+
+
+
+
+

+
+
+ +
+
+

+
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ + + + + + + + + + + +
Nr.StatusTrack
+
+ + + + + + + + + +)V0G0N"; diff --git a/include/player.h b/include/player.h index 7c442da..a35478b 100644 --- a/include/player.h +++ b/include/player.h @@ -5,6 +5,10 @@ #include "spi_master.h" #include "playlist.h" +class Player; + +#include "controller.h" + #define SCI_MODE 0x00 #define SCI_STATUS 0x01 #define SCI_BASS 0x02 @@ -39,7 +43,6 @@ private: enum state { uninitialized, idle, playing, stopping, sleeping, recording }; void _reset(); - void _init(); void _wait(); uint16_t _read_control_register(uint8_t address, bool do_wait=true); void _write_control_register(uint8_t address, uint16_t value, bool do_wait=true); @@ -70,27 +73,32 @@ private: SPISettings* _spi_settings = &_spi_settings_slow; File _file; + uint32_t _file_size = 0; uint8_t _buffer[32]; - uint32_t _current_play_position; - Playlist* _current_playlist; + uint32_t _current_play_position = 0; + Playlist* _current_playlist = NULL; uint _refills; uint8_t _volume; uint16_t _stop_delay; uint32_t _skip_to; SPIMaster* _spi; + Controller* _controller; unsigned long _stopped_at; public: Player(SPIMaster* s); + void init(); + void register_controller(Controller* c); void vol_up(); void vol_down(); void track_next(); void track_prev(); + void set_track(uint8_t track); bool is_playing(); bool play(); bool play(Playlist* p); void stop(bool turn_speaker_off=true); bool loop(); void set_volume(uint8_t vol, bool save = true); - uint32_t position() { return _current_play_position; } - uint8_t volume() { return _volume; } + String position_json(); + String json(); }; diff --git a/include/playlist.h b/include/playlist.h index c034246..ba7f81e 100644 --- a/include/playlist.h +++ b/include/playlist.h @@ -1,25 +1,32 @@ #pragma once #include #include +#include class Playlist { private: uint32_t _position = 0; uint32_t _current_track = 0; + bool _started = false; bool _shuffled = false; std::vector _files; public: Playlist(String path); + void start(); bool has_track_next(); bool has_track_prev(); bool track_next(); bool track_prev(); void track_restart(); + bool set_track(uint8_t track); void reset(); bool is_empty(); String get_current_file(); uint32_t get_position(); void set_position(uint32_t p); void shuffle(uint8_t random_offset=0); + void advent_shuffle(uint8_t day); bool is_fresh(); + void dump(); + void json(JsonObject json); }; diff --git a/include/playlist_manager.h b/include/playlist_manager.h index 32dc323..d642c7c 100644 --- a/include/playlist_manager.h +++ b/include/playlist_manager.h @@ -1,14 +1,18 @@ #pragma once #include +#include #include "playlist.h" class PlaylistManager { private: std::map _map; std::map _playlists; + std::vector _unmapped_folders; public: PlaylistManager(); Playlist* get_playlist_for_id(String id); + Playlist* get_playlist_for_folder(String folder); void dump_ids(); + String json(); }; diff --git a/lib/esp8266FTPServer/ESP8266FtpServer.cpp b/lib/esp8266FTPServer/ESP8266FtpServer.cpp deleted file mode 100644 index 76cf11d..0000000 --- a/lib/esp8266FTPServer/ESP8266FtpServer.cpp +++ /dev/null @@ -1,988 +0,0 @@ -/* - * FTP Serveur for ESP8266 - * based on FTP Serveur for Arduino Due and Ethernet shield (W5100) or WIZ820io (W5200) - * based on Jean-Michel Gallego's work - * modified to work with esp8266 SPIFFS by David Paiva david@nailbuster.com - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "ESP8266FtpServer.h" -#ifdef ESP8266 -#include -#elif defined ESP32 -#include -#include "SPIFFS.h" -#endif -#include -#include -#include - - - - -WiFiServer ftpServer( FTP_CTRL_PORT ); -WiFiServer dataServer( FTP_DATA_PORT_PASV ); - -void FtpServer::begin(String uname, String pword) -{ - // Tells the ftp server to begin listening for incoming connection - _FTP_USER=uname; - _FTP_PASS = pword; - - ftpServer.begin(); - delay(10); - dataServer.begin(); - delay(10); - millisTimeOut = (uint32_t)FTP_TIME_OUT * 60 * 1000; - millisDelay = 0; - cmdStatus = 0; - iniVariables(); -} - -void FtpServer::iniVariables() -{ - // Default for data port - dataPort = FTP_DATA_PORT_PASV; - - // Default Data connection is Active - dataPassiveConn = true; - - // Set the root directory - strcpy( cwdName, "/" ); - - rnfrCmd = false; - transferStatus = 0; - -} - -void FtpServer::handleFTP() -{ - if((int32_t) ( millisDelay - millis() ) > 0 ) - return; - - if (ftpServer.hasClient()) { - client.stop(); - client = ftpServer.available(); - } - - if( cmdStatus == 0 ) - { - if( client.connected()) - disconnectClient(); - cmdStatus = 1; - } - else if( cmdStatus == 1 ) // Ftp server waiting for connection - { - abortTransfer(); - iniVariables(); - #ifdef FTP_DEBUG - Serial.println("Ftp server waiting for connection on port "+ String(FTP_CTRL_PORT)); - #endif - cmdStatus = 2; - } - else if( cmdStatus == 2 ) // Ftp server idle - { - - if( client.connected() ) // A client connected - { - clientConnected(); - millisEndConnection = millis() + 10 * 1000 ; // wait client id during 10 s. - cmdStatus = 3; - } - } - else if( readChar() > 0 ) // got response - { - if( cmdStatus == 3 ) // Ftp server waiting for user identity - if( userIdentity() ) - cmdStatus = 4; - else - cmdStatus = 0; - else if( cmdStatus == 4 ) // Ftp server waiting for user registration - if( userPassword() ) - { - cmdStatus = 5; - millisEndConnection = millis() + millisTimeOut; - } - else - cmdStatus = 0; - else if( cmdStatus == 5 ) // Ftp server waiting for user command - if( ! processCommand()) - cmdStatus = 0; - else - millisEndConnection = millis() + millisTimeOut; - } - else if (!client.connected() || !client) - { - cmdStatus = 1; - #ifdef FTP_DEBUG - Serial.println("client disconnected"); - #endif - } - - if( transferStatus == 1 ) // Retrieve data - { - if( ! doRetrieve()) - transferStatus = 0; - } - else if( transferStatus == 2 ) // Store data - { - if( ! doStore()) - transferStatus = 0; - } - else if( cmdStatus > 2 && ! ((int32_t) ( millisEndConnection - millis() ) > 0 )) - { - client.println("530 Timeout"); - millisDelay = millis() + 200; // delay of 200 ms - cmdStatus = 0; - } -} - -void FtpServer::clientConnected() -{ - #ifdef FTP_DEBUG - Serial.println("Client connected!"); - #endif - client.println( "220--- Welcome to FTP for ESP8266/ESP32 ---"); - client.println( "220--- By David Paiva ---"); - client.println( "220 -- Version "+ String(FTP_SERVER_VERSION) +" --"); - iCL = 0; -} - -void FtpServer::disconnectClient() -{ - #ifdef FTP_DEBUG - Serial.println(" Disconnecting client"); - #endif - abortTransfer(); - client.println("221 Goodbye"); - client.stop(); -} - -boolean FtpServer::userIdentity() -{ - if( strcmp( command, "USER" )) - client.println( "500 Syntax error"); - if( strcmp( parameters, _FTP_USER.c_str() )) - client.println( "530 user not found"); - else - { - client.println( "331 OK. Password required"); - strcpy( cwdName, "/" ); - return true; - } - millisDelay = millis() + 100; // delay of 100 ms - return false; -} - -boolean FtpServer::userPassword() -{ - if( strcmp( command, "PASS" )) - client.println( "500 Syntax error"); - else if( strcmp( parameters, _FTP_PASS.c_str() )) - client.println( "530 "); - else - { - #ifdef FTP_DEBUG - Serial.println( "OK. Waiting for commands."); - #endif - client.println( "230 OK."); - return true; - } - millisDelay = millis() + 100; // delay of 100 ms - return false; -} - -boolean FtpServer::processCommand() -{ - /////////////////////////////////////// - // // - // ACCESS CONTROL COMMANDS // - // // - /////////////////////////////////////// - - // - // CDUP - Change to Parent Directory - // - if( ! strcmp( command, "CDUP" )) - { - char* pos = strrchr(cwdName, '/'); - if (pos > cwdName) cwdName[pos-cwdName]='\0'; - else if (pos==cwdName) cwdName[1]='\0'; - client.println("250 Ok. Current directory is " + String(cwdName)); - } - // - // CWD - Change Working Directory - // - else if( ! strcmp( command, "CWD" )) - { - char path[ FTP_CWD_SIZE ]; - - if( strcmp( parameters, "." ) == 0 ) // 'CWD .' is the same as PWD command - client.println( "257 \"" + String(cwdName) + "\" is your current directory"); - else if (strcmp(parameters, "..")==0) { - char* pos = strrchr(cwdName, '/'); - if (pos > cwdName) cwdName[pos-cwdName]='\0'; - else if (pos==cwdName) cwdName[1]='\0'; - client.println( "250 Ok. Current directory is " + String(cwdName) ); - } else - { - if (makePath(path)) { - if (SD.exists(path)) { - File f = SD.open(path); - if (f.isDirectory()) { - strcpy(cwdName, path); - client.println( "250 Ok. Current directory is " + String(cwdName) ); - } else { - client.println("450 Path is not a directory."); - } - f.close(); - } else { - client.println("450 path does not exist"); - } - } else { - client.println( "450 Invalid path"); - } - } - - } - // - // PWD - Print Directory - // - else if( ! strcmp( command, "PWD" )) - client.println( "257 \"" + String(cwdName) + "\" is your current directory"); - // - // QUIT - // - else if( ! strcmp( command, "QUIT" )) - { - disconnectClient(); - return false; - } - - /////////////////////////////////////// - // // - // TRANSFER PARAMETER COMMANDS // - // // - /////////////////////////////////////// - - // - // MODE - Transfer Mode - // - else if( ! strcmp( command, "MODE" )) - { - if( ! strcmp( parameters, "S" )) - client.println( "200 S Ok"); - // else if( ! strcmp( parameters, "B" )) - // client.println( "200 B Ok\r\n"; - else - client.println( "504 Only S(tream) is suported"); - } - // - // PASV - Passive Connection management - // - else if( ! strcmp( command, "PASV" )) - { - if (data.connected()) data.stop(); - //dataServer.begin(); - //dataIp = Ethernet.localIP(); - dataIp = client.localIP(); - dataPort = FTP_DATA_PORT_PASV; - //data.connect( dataIp, dataPort ); - //data = dataServer.available(); - #ifdef FTP_DEBUG - Serial.println("Connection management set to passive"); - Serial.println( "Data port set to " + String(dataPort)); - #endif - client.println( "227 Entering Passive Mode ("+ String(dataIp[0]) + "," + String(dataIp[1])+","+ String(dataIp[2])+","+ String(dataIp[3])+","+String( dataPort >> 8 ) +","+String ( dataPort & 255 )+")."); - dataPassiveConn = true; - } - // - // PORT - Data Port - // - else if( ! strcmp( command, "PORT" )) - { - if (data) data.stop(); - // get IP of data client - dataIp[ 0 ] = atoi( parameters ); - char * p = strchr( parameters, ',' ); - for( uint8_t i = 1; i < 4; i ++ ) - { - dataIp[ i ] = atoi( ++ p ); - p = strchr( p, ',' ); - } - // get port of data client - dataPort = 256 * atoi( ++ p ); - p = strchr( p, ',' ); - dataPort += atoi( ++ p ); - if( p == NULL ) - client.println( "501 Can't interpret parameters"); - else - { - - client.println("200 PORT command successful"); - dataPassiveConn = false; - } - } - // - // STRU - File Structure - // - else if( ! strcmp( command, "STRU" )) - { - if( ! strcmp( parameters, "F" )) - client.println( "200 F Ok"); - // else if( ! strcmp( parameters, "R" )) - // client.println( "200 B Ok\r\n"; - else - client.println( "504 Only F(ile) is suported"); - } - // - // TYPE - Data Type - // - else if( ! strcmp( command, "TYPE" )) - { - if( ! strcmp( parameters, "A" )) - client.println( "200 TYPE is now ASII"); - else if( ! strcmp( parameters, "I" )) - client.println( "200 TYPE is now 8-bit binary"); - else - client.println( "504 Unknow TYPE"); - } - - /////////////////////////////////////// - // // - // FTP SERVICE COMMANDS // - // // - /////////////////////////////////////// - - // - // ABOR - Abort - // - else if( ! strcmp( command, "ABOR" )) - { - abortTransfer(); - client.println( "226 Data connection closed"); - } - // - // DELE - Delete a File - // - else if( ! strcmp( command, "DELE" )) - { - char path[ FTP_CWD_SIZE ]; - if( strlen( parameters ) == 0 ) - client.println( "501 No file name"); - else if( makePath( path )) - { - if( ! SD.exists( path )) - client.println( "550 File " + String(parameters) + " not found"); - else - { - if( SD.remove( path )) - client.println( "250 Deleted " + String(parameters) ); - else - client.println( "450 Can't delete " + String(parameters)); - } - } - } - // - // LIST - List - // - else if( ! strcmp( command, "LIST" )) - { - if( ! dataConnect()) - client.println( "425 No data connection"); - else - { - client.println( "150 Accepted data connection"); - uint16_t nm = 0; - File root = SD.open(cwdName); - if(!root){ - client.println( "550 Can't open directory " + String(cwdName) ); - // return; - } else { - // if(!root.isDirectory()){ - // Serial.println("Not a directory"); - // return; - // } - - File file = root.openNextFile(); - while(file){ - data.printf("%s %13d Jan 01 00:00 %s\r\n", file.isDirectory() ? "drwxr-xr-x 1 owner group" : "-rw-r--r-- 1 owner group", file.size(), file.name()); - file = root.openNextFile(); - } - client.println( "226 " + String(nm) + " matches total"); - } - data.stop(); - } - } - // - // MLSD - Listing for Machine Processing (see RFC 3659) - // - else if( ! strcmp( command, "MLSD" )) - { - if( ! dataConnect()) - client.println( "425 No data connection MLSD"); - else - { - client.println( "150 Accepted data connection"); - uint16_t nm = 0; - File root = SD.open(cwdName); - // if(!root){ - // client.println( "550 Can't open directory " + String(cwdName) ); - // // return; - // } else { - // if(!root.isDirectory()){ - // Serial.println("Not a directory"); - // return; - // } - - File file = root.openNextFile(); - while(file){ - if(file.isDirectory()){ - // if(levels){ - // listDir(fs, file.name(), levels -1); - // } - data.println(String("Type=dir;perm=cdelmp;modify=20000101160656; ") + file.name()); - } else { - String fn, fs; - fn = file.name(); - fs = String(file.size()); - data.println( "Type=file;perm=drw;Size=" + fs + ";modify=20000101160656;" +" " + fn); - nm ++; - } - file = root.openNextFile(); - } - client.println( "226-options: -a -l"); - client.println( "226 " + String(nm) + " matches total"); - // } - data.stop(); - } - } - // - // NLST - Name List - // - else if( ! strcmp( command, "NLST" )) - { - if( ! dataConnect()) - client.println( "425 No data connection"); - else - { - client.println( "150 Accepted data connection"); - uint16_t nm = 0; - File root = SD.open(cwdName); - if(!root){ - client.println( "550 Can't open directory " + String(cwdName) ); - } else { - - File file = root.openNextFile(); - while(file){ - data.println( file.name()); - nm ++; - file = root.openNextFile(); - } - client.println( "226 " + String(nm) + " matches total"); - } - data.stop(); - } - } - // - // NOOP - // - else if( ! strcmp( command, "NOOP" )) - { - // dataPort = 0; - client.println( "200 Zzz..."); - } - // - // RETR - Retrieve - // - else if( ! strcmp( command, "RETR" )) - { - char path[ FTP_CWD_SIZE ]; - if( strlen( parameters ) == 0 ) - client.println( "501 No file name"); - else if( makePath( path )) - { - file = SD.open(path, FILE_READ); - if( !file) - client.println( "550 File " +String(parameters)+ " not found"); - else if( !file ) - client.println( "450 Can't open " +String(parameters)); - else if( ! dataConnect()) - client.println( "425 No data connection"); - else - { - #ifdef FTP_DEBUG - Serial.println("Sending " + String(parameters)); - #endif - client.println( "150-Connected to port "+ String(dataPort)); - client.println( "150 " + String(file.size()) + " bytes to download"); - millisBeginTrans = millis(); - bytesTransfered = 0; - transferStatus = 1; - } - } - } - // - // STOR - Store - // - else if( ! strcmp( command, "STOR" )) - { - char path[ FTP_CWD_SIZE ]; - if( strlen( parameters ) == 0 ) - client.println( "501 No file name"); - else if( makePath( path )) - { - file = SD.open(path, FILE_WRITE); - if( !file) - client.println( "451 Can't open/create " +String(parameters) ); - else if( ! dataConnect()) - { - client.println( "425 No data connection"); - file.close(); - } - else - { - #ifdef FTP_DEBUG - Serial.println( "Receiving " +String(parameters)); - #endif - client.println( "150 Connected to port " + String(dataPort)); - millisBeginTrans = millis(); - bytesTransfered = 0; - transferStatus = 2; - } - } - } - // - // MKD - Make Directory - // - else if( ! strcmp( command, "MKD" )) - { - char path[ FTP_CWD_SIZE ]; - if( strlen( parameters ) == 0 ) - client.println( "501 No file name"); - else if( makePath( path )) { - if (SD.exists(path)) { - client.println("550 Directory already exists"); - } else { - SD.mkdir(path); - client.println("257 Directory created"); - } - } else { - client.println( "550 Can't create \"" + String(parameters) + "\" - malformed path?"); //not support on espyet - } - } - // - // RMD - Remove a Directory - // - else if( ! strcmp( command, "RMD" )) - { - client.println( "501 Can't delete \"" +String(parameters)); - - } - // - // RNFR - Rename From - // - else if( ! strcmp( command, "RNFR" )) - { - client.println("500 Rename not supported"); - /*buf[ 0 ] = 0; - if( strlen( parameters ) == 0 ) - client.println( "501 No file name"); - else if( makePath( buf )) - { - if( ! SD.exists( buf )) - client.println( "550 File " +String(parameters)+ " not found"); - else - { - #ifdef FTP_DEBUG - Serial.println("Renaming " + String(buf)); - #endif - client.println( "350 RNFR accepted - file exists, ready for destination"); - rnfrCmd = true; - } - }*/ - } - // - // RNTO - Rename To - // - else if( ! strcmp( command, "RNTO" )) - { - client.println("500 Rename not supported"); - /*char path[ FTP_CWD_SIZE ]; - char dir[ FTP_FIL_SIZE ]; - if( strlen( buf ) == 0 || ! rnfrCmd ) - client.println( "503 Need RNFR before RNTO"); - else if( strlen( parameters ) == 0 ) - client.println( "501 No file name"); - else - - else if( makePath( path )) - { - if( SD.exists( path )) - client.println( "553 " +String(parameters)+ " already exists"); - else - { - #ifdef FTP_DEBUG - Serial.println("Renaming " + String(buf) + " to " + String(path)); - #endif - if( SD.rename( buf, path )) - client.println( "250 File successfully renamed or moved"); - else - client.println( "451 Rename/move failure"); - - } - }*/ - rnfrCmd = false; - } - - /////////////////////////////////////// - // // - // EXTENSIONS COMMANDS (RFC 3659) // - // // - /////////////////////////////////////// - - // - // FEAT - New Features - // - else if( ! strcmp( command, "FEAT" )) - { - client.println( "211-Extensions suported:"); - client.println( " MLSD"); - client.println( "211 End."); - } - // - // MDTM - File Modification Time (see RFC 3659) - // - else if (!strcmp(command, "MDTM")) - { - client.println("550 Unable to retrieve time"); - } - - // - // SIZE - Size of the file - // - else if( ! strcmp( command, "SIZE" )) - { - char path[ FTP_CWD_SIZE ]; - if( strlen( parameters ) == 0 ) - client.println( "501 No file name"); - else if( makePath( path )) - { - file = SD.open(path, FILE_READ); - if(!file) - client.println( "450 Can't open " +String(parameters) ); - else - { - client.println( "213 " + String(file.size())); - file.close(); - } - } - } - // - // SITE - System command - // - else if( ! strcmp( command, "SITE" )) - { - client.println( "500 Unknow SITE command " +String(parameters) ); - } - // - // Unrecognized commands ... - // - else - client.println( "500 Unknow command"); - - return true; -} - -boolean FtpServer::dataConnect() -{ - unsigned long startTime = millis(); - //wait 5 seconds for a data connection - if (!data.connected()) - { - while (!dataServer.hasClient() && millis() - startTime < 10000) - { - //delay(100); - yield(); - } - if (dataServer.hasClient()) { - data.stop(); - data = dataServer.available(); - #ifdef FTP_DEBUG - Serial.println("ftpdataserver client...."); - #endif - - } - } - - return data.connected(); - -} - -boolean FtpServer::doRetrieve() -{ -if (data.connected()) -{ - int16_t nb = file.readBytes(buf, FTP_BUF_SIZE); - if (nb > 0) - { - data.write((uint8_t*)buf, nb); - bytesTransfered += nb; - return true; - } -} -closeTransfer(); -return false; -} - -boolean FtpServer::doStore() -{ - // Avoid blocking by never reading more bytes than are available - int navail = data.available(); - - if (navail > 0) - { - // And be sure not to overflow buf. - if (navail > FTP_BUF_SIZE) navail = FTP_BUF_SIZE; - int16_t nb = data.read((uint8_t*) buf, navail ); - // int16_t nb = data.readBytes((uint8_t*) buf, FTP_BUF_SIZE ); - if( nb > 0 ) - { - // Serial.println( millis() << " " << nb << endl; - file.write((uint8_t*) buf, nb ); - bytesTransfered += nb; - } - } - if( !data.connected() && (navail <= 0) ) - { - closeTransfer(); - return false; - } - else - { - return true; - } -} - -void FtpServer::closeTransfer() -{ - uint32_t deltaT = (int32_t) ( millis() - millisBeginTrans ); - if( deltaT > 0 && bytesTransfered > 0 ) - { - client.println( "226-File successfully transferred"); - client.println( "226 " + String(deltaT) + " ms, "+ String(bytesTransfered / deltaT) + " kbytes/s"); - } - else - client.println( "226 File successfully transferred"); - - file.close(); - data.stop(); -} - -void FtpServer::abortTransfer() -{ - if( transferStatus > 0 ) - { - file.close(); - data.stop(); - client.println( "426 Transfer aborted" ); - #ifdef FTP_DEBUG - Serial.println( "Transfer aborted!") ; - #endif - } - transferStatus = 0; -} - -// Read a char from client connected to ftp server -// -// update cmdLine and command buffers, iCL and parameters pointers -// -// return: -// -2 if buffer cmdLine is full -// -1 if line not completed -// 0 if empty line received -// length of cmdLine (positive) if no empty line received - -int8_t FtpServer::readChar() -{ - int8_t rc = -1; - - if( client.available()) - { - char c = client.read(); - // char c; - // client.readBytes((uint8_t*) c, 1); - #ifdef FTP_DEBUG - Serial.print( c); - #endif - if( c == '\\' ) - c = '/'; - if( c != '\r' ) - if( c != '\n' ) - { - if( iCL < FTP_CMD_SIZE ) - cmdLine[ iCL ++ ] = c; - else - rc = -2; // Line too long - } - else - { - cmdLine[ iCL ] = 0; - command[ 0 ] = 0; - parameters = NULL; - // empty line? - if( iCL == 0 ) - rc = 0; - else - { - rc = iCL; - // search for space between command and parameters - parameters = strchr( cmdLine, ' ' ); - if( parameters != NULL ) - { - if( parameters - cmdLine > 4 ) - rc = -2; // Syntax error - else - { - strncpy( command, cmdLine, parameters - cmdLine ); - command[ parameters - cmdLine ] = 0; - - while( * ( ++ parameters ) == ' ' ) - ; - } - } - else if( strlen( cmdLine ) > 4 ) - rc = -2; // Syntax error. - else - strcpy( command, cmdLine ); - iCL = 0; - } - } - if( rc > 0 ) - for( uint8_t i = 0 ; i < strlen( command ); i ++ ) - command[ i ] = toupper( command[ i ] ); - if( rc == -2 ) - { - iCL = 0; - client.println( "500 Syntax error"); - } - } - return rc; -} - -// Make complete path/name from cwdName and parameters -// -// 3 possible cases: parameters can be absolute path, relative path or only the name -// -// parameters: -// fullName : where to store the path/name -// -// return: -// true, if done - -boolean FtpServer::makePath( char * fullName ) -{ - return makePath( fullName, parameters ); -} - -boolean FtpServer::makePath( char * fullName, char * param ) -{ - if( param == NULL ) - param = parameters; - - // Root or empty? - if( strcmp( param, "/" ) == 0 || strlen( param ) == 0 ) - { - strcpy( fullName, "/" ); - return true; - } - // If relative path, concatenate with current dir - if( param[0] != '/' ) - { - strcpy( fullName, cwdName ); - if( fullName[ strlen( fullName ) - 1 ] != '/' ) - strncat( fullName, "/", FTP_CWD_SIZE ); - strncat( fullName, param, FTP_CWD_SIZE ); - } - else - strcpy( fullName, param ); - // If ends with '/', remove it - uint16_t strl = strlen( fullName ) - 1; - if( fullName[ strl ] == '/' && strl > 1 ) - fullName[ strl ] = 0; - if( strlen( fullName ) < FTP_CWD_SIZE ) - return true; - - client.println( "500 Command line too long"); - return false; -} - -// Calculate year, month, day, hour, minute and second -// from first parameter sent by MDTM command (YYYYMMDDHHMMSS) -// -// parameters: -// pyear, pmonth, pday, phour, pminute and psecond: pointer of -// variables where to store data -// -// return: -// 0 if parameter is not YYYYMMDDHHMMSS -// length of parameter + space - -uint8_t FtpServer::getDateTime( uint16_t * pyear, uint8_t * pmonth, uint8_t * pday, - uint8_t * phour, uint8_t * pminute, uint8_t * psecond ) -{ - char dt[ 15 ]; - - // Date/time are expressed as a 14 digits long string - // terminated by a space and followed by name of file - if( strlen( parameters ) < 15 || parameters[ 14 ] != ' ' ) - return 0; - for( uint8_t i = 0; i < 14; i++ ) - if( ! isdigit( parameters[ i ])) - return 0; - - strncpy( dt, parameters, 14 ); - dt[ 14 ] = 0; - * psecond = atoi( dt + 12 ); - dt[ 12 ] = 0; - * pminute = atoi( dt + 10 ); - dt[ 10 ] = 0; - * phour = atoi( dt + 8 ); - dt[ 8 ] = 0; - * pday = atoi( dt + 6 ); - dt[ 6 ] = 0 ; - * pmonth = atoi( dt + 4 ); - dt[ 4 ] = 0 ; - * pyear = atoi( dt ); - return 15; -} - -// Create string YYYYMMDDHHMMSS from date and time -// -// parameters: -// date, time -// tstr: where to store the string. Must be at least 15 characters long -// -// return: -// pointer to tstr - -char * FtpServer::makeDateTimeStr( char * tstr, uint16_t date, uint16_t time ) -{ - sprintf( tstr, "%04u%02u%02u%02u%02u%02u", - (( date & 0xFE00 ) >> 9 ) + 1980, ( date & 0x01E0 ) >> 5, date & 0x001F, - ( time & 0xF800 ) >> 11, ( time & 0x07E0 ) >> 5, ( time & 0x001F ) << 1 ); - return tstr; -} - diff --git a/lib/esp8266FTPServer/ESP8266FtpServer.cppRMD b/lib/esp8266FTPServer/ESP8266FtpServer.cppRMD deleted file mode 100644 index 8d80b41..0000000 --- a/lib/esp8266FTPServer/ESP8266FtpServer.cppRMD +++ /dev/null @@ -1,1021 +0,0 @@ -/* - * FTP Serveur for ESP8266 - * based on FTP Serveur for Arduino Due and Ethernet shield (W5100) or WIZ820io (W5200) - * based on Jean-Michel Gallego's work - * modified to work with esp8266 SPIFFS by David Paiva david@nailbuster.com - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "ESP8266FtpServer.h" -#ifdef ESP8266 -#include -#elif defined ESP32 -#include -#include "SPIFFS.h" -#endif -#include -#include -#include - - - - -WiFiServer ftpServer( FTP_CTRL_PORT ); -WiFiServer dataServer( FTP_DATA_PORT_PASV ); - -void FtpServer::begin(String uname, String pword) -{ - // Tells the ftp server to begin listening for incoming connection - _FTP_USER=uname; - _FTP_PASS = pword; - - ftpServer.begin(); - delay(10); - dataServer.begin(); - delay(10); - millisTimeOut = (uint32_t)FTP_TIME_OUT * 60 * 1000; - millisDelay = 0; - cmdStatus = 0; - iniVariables(); -} - -void FtpServer::iniVariables() -{ - // Default for data port - dataPort = FTP_DATA_PORT_PASV; - - // Default Data connection is Active - dataPassiveConn = true; - - // Set the root directory - strcpy( cwdName, "/" ); - - rnfrCmd = false; - transferStatus = 0; - -} - -void FtpServer::handleFTP() -{ - if((int32_t) ( millisDelay - millis() ) > 0 ) - return; - - if (ftpServer.hasClient()) { - client.stop(); - client = ftpServer.available(); - } - - if( cmdStatus == 0 ) - { - if( client.connected()) - disconnectClient(); - cmdStatus = 1; - } - else if( cmdStatus == 1 ) // Ftp server waiting for connection - { - abortTransfer(); - iniVariables(); - #ifdef FTP_DEBUG - Serial.println("Ftp server waiting for connection on port "+ String(FTP_CTRL_PORT)); - #endif - cmdStatus = 2; - } - else if( cmdStatus == 2 ) // Ftp server idle - { - - if( client.connected() ) // A client connected - { - clientConnected(); - millisEndConnection = millis() + 10 * 1000 ; // wait client id during 10 s. - cmdStatus = 3; - } - } - else if( readChar() > 0 ) // got response - { - if( cmdStatus == 3 ) // Ftp server waiting for user identity - if( userIdentity() ) - cmdStatus = 4; - else - cmdStatus = 0; - else if( cmdStatus == 4 ) // Ftp server waiting for user registration - if( userPassword() ) - { - cmdStatus = 5; - millisEndConnection = millis() + millisTimeOut; - } - else - cmdStatus = 0; - else if( cmdStatus == 5 ) // Ftp server waiting for user command - if( ! processCommand()) - cmdStatus = 0; - else - millisEndConnection = millis() + millisTimeOut; - } - else if (!client.connected() || !client) - { - cmdStatus = 1; - #ifdef FTP_DEBUG - Serial.println("client disconnected"); - #endif - } - - if( transferStatus == 1 ) // Retrieve data - { - if( ! doRetrieve()) - transferStatus = 0; - } - else if( transferStatus == 2 ) // Store data - { - if( ! doStore()) - transferStatus = 0; - } - else if( cmdStatus > 2 && ! ((int32_t) ( millisEndConnection - millis() ) > 0 )) - { - client.println("530 Timeout"); - millisDelay = millis() + 200; // delay of 200 ms - cmdStatus = 0; - } -} - -void FtpServer::clientConnected() -{ - #ifdef FTP_DEBUG - Serial.println("Client connected!"); - #endif - client.println( "220--- Welcome to FTP for ESP8266/ESP32 ---"); - client.println( "220--- By David Paiva ---"); - client.println( "220 -- Version "+ String(FTP_SERVER_VERSION) +" --"); - iCL = 0; -} - -void FtpServer::disconnectClient() -{ - #ifdef FTP_DEBUG - Serial.println(" Disconnecting client"); - #endif - abortTransfer(); - client.println("221 Goodbye"); - client.stop(); -} - -boolean FtpServer::userIdentity() -{ - if( strcmp( command, "USER" )) - client.println( "500 Syntax error"); - if( strcmp( parameters, _FTP_USER.c_str() )) - client.println( "530 user not found"); - else - { - client.println( "331 OK. Password required"); - strcpy( cwdName, "/" ); - return true; - } - millisDelay = millis() + 100; // delay of 100 ms - return false; -} - -boolean FtpServer::userPassword() -{ - if( strcmp( command, "PASS" )) - client.println( "500 Syntax error"); - else if( strcmp( parameters, _FTP_PASS.c_str() )) - client.println( "530 "); - else - { - #ifdef FTP_DEBUG - Serial.println( "OK. Waiting for commands."); - #endif - client.println( "230 OK."); - return true; - } - millisDelay = millis() + 100; // delay of 100 ms - return false; -} - -boolean FtpServer::processCommand() -{ - /////////////////////////////////////// - // // - // ACCESS CONTROL COMMANDS // - // // - /////////////////////////////////////// - - // - // CDUP - Change to Parent Directory - // - if( ! strcmp( command, "CDUP" )) - { - char* pos = strrchr(cwdName, '/'); - if (pos > cwdName) cwdName[pos-cwdName]='\0'; - else if (pos==cwdName) cwdName[1]='\0'; - client.println("250 Ok. Current directory is " + String(cwdName)); - } - // - // CWD - Change Working Directory - // - else if( ! strcmp( command, "CWD" )) - { - char path[ FTP_CWD_SIZE ]; - - if( strcmp( parameters, "." ) == 0 ) // 'CWD .' is the same as PWD command - client.println( "257 \"" + String(cwdName) + "\" is your current directory"); - else if (strcmp(parameters, "..")==0) { - char* pos = strrchr(cwdName, '/'); - if (pos > cwdName) cwdName[pos-cwdName]='\0'; - else if (pos==cwdName) cwdName[1]='\0'; - client.println( "250 Ok. Current directory is " + String(cwdName) ); - } else - { - if (makePath(path)) { - if (SD.exists(path)) { - File f = SD.open(path); - if (f.isDirectory()) { - strcpy(cwdName, path); - client.println( "250 Ok. Current directory is " + String(cwdName) ); - } else { - client.println("450 Path is not a directory."); - } - f.close(); - } else { - client.println("450 path does not exist"); - } - } else { - client.println( "450 Invalid path"); - } - } - - } - // - // PWD - Print Directory - // - else if( ! strcmp( command, "PWD" )) - client.println( "257 \"" + String(cwdName) + "\" is your current directory"); - // - // QUIT - // - else if( ! strcmp( command, "QUIT" )) - { - disconnectClient(); - return false; - } - - /////////////////////////////////////// - // // - // TRANSFER PARAMETER COMMANDS // - // // - /////////////////////////////////////// - - // - // MODE - Transfer Mode - // - else if( ! strcmp( command, "MODE" )) - { - if( ! strcmp( parameters, "S" )) - client.println( "200 S Ok"); - // else if( ! strcmp( parameters, "B" )) - // client.println( "200 B Ok\r\n"; - else - client.println( "504 Only S(tream) is suported"); - } - // - // PASV - Passive Connection management - // - else if( ! strcmp( command, "PASV" )) - { - if (data.connected()) data.stop(); - //dataServer.begin(); - //dataIp = Ethernet.localIP(); - dataIp = client.localIP(); - dataPort = FTP_DATA_PORT_PASV; - //data.connect( dataIp, dataPort ); - //data = dataServer.available(); - #ifdef FTP_DEBUG - Serial.println("Connection management set to passive"); - Serial.println( "Data port set to " + String(dataPort)); - #endif - client.println( "227 Entering Passive Mode ("+ String(dataIp[0]) + "," + String(dataIp[1])+","+ String(dataIp[2])+","+ String(dataIp[3])+","+String( dataPort >> 8 ) +","+String ( dataPort & 255 )+")."); - dataPassiveConn = true; - } - // - // PORT - Data Port - // - else if( ! strcmp( command, "PORT" )) - { - if (data) data.stop(); - // get IP of data client - dataIp[ 0 ] = atoi( parameters ); - char * p = strchr( parameters, ',' ); - for( uint8_t i = 1; i < 4; i ++ ) - { - dataIp[ i ] = atoi( ++ p ); - p = strchr( p, ',' ); - } - // get port of data client - dataPort = 256 * atoi( ++ p ); - p = strchr( p, ',' ); - dataPort += atoi( ++ p ); - if( p == NULL ) - client.println( "501 Can't interpret parameters"); - else - { - - client.println("200 PORT command successful"); - dataPassiveConn = false; - } - } - // - // STRU - File Structure - // - else if( ! strcmp( command, "STRU" )) - { - if( ! strcmp( parameters, "F" )) - client.println( "200 F Ok"); - // else if( ! strcmp( parameters, "R" )) - // client.println( "200 B Ok\r\n"; - else - client.println( "504 Only F(ile) is suported"); - } - // - // TYPE - Data Type - // - else if( ! strcmp( command, "TYPE" )) - { - if( ! strcmp( parameters, "A" )) - client.println( "200 TYPE is now ASII"); - else if( ! strcmp( parameters, "I" )) - client.println( "200 TYPE is now 8-bit binary"); - else - client.println( "504 Unknow TYPE"); - } - - /////////////////////////////////////// - // // - // FTP SERVICE COMMANDS // - // // - /////////////////////////////////////// - - // - // ABOR - Abort - // - else if( ! strcmp( command, "ABOR" )) - { - abortTransfer(); - client.println( "226 Data connection closed"); - } - // - // DELE - Delete a File - // - else if( ! strcmp( command, "DELE" )) - { - char path[ FTP_CWD_SIZE ]; - if( strlen( parameters ) == 0 ) - client.println( "501 No file name"); - else if( makePath( path )) - { - if( ! SD.exists( path )) - client.println( "550 File " + String(parameters) + " not found"); - else - { - if( SD.remove( path )) - client.println( "250 Deleted " + String(parameters) ); - else - client.println( "450 Can't delete " + String(parameters)); - } - } - } - // - // LIST - List - // - else if( ! strcmp( command, "LIST" )) - { - if( ! dataConnect()) - client.println( "425 No data connection"); - else - { - client.println( "150 Accepted data connection"); - uint16_t nm = 0; - File root = SD.open(cwdName); - if(!root){ - client.println( "550 Can't open directory " + String(cwdName) ); - // return; - } else { - // if(!root.isDirectory()){ - // Serial.println("Not a directory"); - // return; - // } - - File file = root.openNextFile(); - while(file){ - if (parameters && strcmp(parameters, "-la")==0) { - data.printf("%s %13d Jan 01 00:00 %s\r\n", file.isDirectory() ? "drwxr-xr-x 1 owner group" : "-rw-r--r-- 1 owner group", file.size(), file.name()); - } else { - if(file.isDirectory()){ - data.println( "+r,s " + String(file.name())); - // Serial.print(" DIR : "); - // Serial.println(file.name()); - // if(levels){ - // listDir(fs, file.name(), levels -1); - // } - } else { - String fn, fs; - fn = file.name(); - // fn.remove(0, 1); - fs = String(file.size()); - data.println( "+r,s" + fs); - data.println( ",\t" + fn ); - nm ++; - } - } - file = root.openNextFile(); - } - client.println( "226 " + String(nm) + " matches total"); - } - data.stop(); - } - } - // - // MLSD - Listing for Machine Processing (see RFC 3659) - // - else if( ! strcmp( command, "MLSD" )) - { - if( ! dataConnect()) - client.println( "425 No data connection MLSD"); - else - { - client.println( "150 Accepted data connection"); - uint16_t nm = 0; - File root = SD.open(cwdName); - // if(!root){ - // client.println( "550 Can't open directory " + String(cwdName) ); - // // return; - // } else { - // if(!root.isDirectory()){ - // Serial.println("Not a directory"); - // return; - // } - - File file = root.openNextFile(); - while(file){ - if(file.isDirectory()){ - // if(levels){ - // listDir(fs, file.name(), levels -1); - // } - data.println(String("Type=dir;perm=cdelmp;modify=20000101160656; ") + file.name()); - } else { - String fn, fs; - fn = file.name(); - fs = String(file.size()); - data.println( "Type=file;perm=drw;Size=" + fs + ";modify=20000101160656;" +" " + fn); - nm ++; - } - file = root.openNextFile(); - } - client.println( "226-options: -a -l"); - client.println( "226 " + String(nm) + " matches total"); - // } - data.stop(); - } - } - // - // NLST - Name List - // - else if( ! strcmp( command, "NLST" )) - { - if( ! dataConnect()) - client.println( "425 No data connection"); - else - { - client.println( "150 Accepted data connection"); - uint16_t nm = 0; - File root = SD.open(cwdName); - if(!root){ - client.println( "550 Can't open directory " + String(cwdName) ); - } else { - - File file = root.openNextFile(); - while(file){ - data.println( file.name()); - nm ++; - file = root.openNextFile(); - } - client.println( "226 " + String(nm) + " matches total"); - } - data.stop(); - } - } - // - // NOOP - // - else if( ! strcmp( command, "NOOP" )) - { - // dataPort = 0; - client.println( "200 Zzz..."); - } - // - // RETR - Retrieve - // - else if( ! strcmp( command, "RETR" )) - { - char path[ FTP_CWD_SIZE ]; - if( strlen( parameters ) == 0 ) - client.println( "501 No file name"); - else if( makePath( path )) - { - file = SD.open(path, FILE_READ); - if( !file) - client.println( "550 File " +String(parameters)+ " not found"); - else if( !file ) - client.println( "450 Can't open " +String(parameters)); - else if( ! dataConnect()) - client.println( "425 No data connection"); - else - { - #ifdef FTP_DEBUG - Serial.println("Sending " + String(parameters)); - #endif - client.println( "150-Connected to port "+ String(dataPort)); - client.println( "150 " + String(file.size()) + " bytes to download"); - millisBeginTrans = millis(); - bytesTransfered = 0; - transferStatus = 1; - } - } - } - // - // STOR - Store - // - else if( ! strcmp( command, "STOR" )) - { - char path[ FTP_CWD_SIZE ]; - if( strlen( parameters ) == 0 ) - client.println( "501 No file name"); - else if( makePath( path )) - { - file = SD.open(path, FILE_WRITE); - if( !file) - client.println( "451 Can't open/create " +String(parameters) ); - else if( ! dataConnect()) - { - client.println( "425 No data connection"); - file.close(); - } - else - { - #ifdef FTP_DEBUG - Serial.println( "Receiving " +String(parameters)); - #endif - client.println( "150 Connected to port " + String(dataPort)); - millisBeginTrans = millis(); - bytesTransfered = 0; - transferStatus = 2; - } - } - } - // - // MKD - Make Directory - // - else if( ! strcmp( command, "MKD" )) - { - char path[ FTP_CWD_SIZE ]; - if( strlen( parameters ) == 0 ) - client.println( "501 No file name"); - else if( makePath( path )) { - if (SD.exists(path)) { - client.println("550 Directory already exists"); - } else { - SD.mkdir(path); - client.println("257 Directory created"); - } - } else { - client.println( "550 Can't create \"" + String(parameters) + "\" - malformed path?"); //not support on espyet - } - } - // - // RMD - Remove a Directory - // - else if( ! strcmp( command, "RMD" )) - { - //client.println( "501 Can't delete \"" +String(parameters)); - char path[ FTP_CWD_SIZE ]; - if (makePath(path)) { - if (SD.exists(path)) { - if (SD.remove(path)) { - client.println("200 Directory deleted"); - } else { - client.println("501 Couldn't delete directory. An unknown error occurred."); - } - } else { - client.println("501 Could not delete dir - does not exist."); - } - } else { - client.println("501 Invalid path"); - } - - } - // - // RNFR - Rename From - // - else if( ! strcmp( command, "RNFR" )) - { - client.println("500 Rename not supported"); - /*buf[ 0 ] = 0; - if( strlen( parameters ) == 0 ) - client.println( "501 No file name"); - else if( makePath( buf )) - { - if( ! SD.exists( buf )) - client.println( "550 File " +String(parameters)+ " not found"); - else - { - #ifdef FTP_DEBUG - Serial.println("Renaming " + String(buf)); - #endif - client.println( "350 RNFR accepted - file exists, ready for destination"); - rnfrCmd = true; - } - }*/ - } - // - // RNTO - Rename To - // - else if( ! strcmp( command, "RNTO" )) - { - client.println("500 Rename not supported"); - /*char path[ FTP_CWD_SIZE ]; - char dir[ FTP_FIL_SIZE ]; - if( strlen( buf ) == 0 || ! rnfrCmd ) - client.println( "503 Need RNFR before RNTO"); - else if( strlen( parameters ) == 0 ) - client.println( "501 No file name"); - else - - else if( makePath( path )) - { - if( SD.exists( path )) - client.println( "553 " +String(parameters)+ " already exists"); - else - { - #ifdef FTP_DEBUG - Serial.println("Renaming " + String(buf) + " to " + String(path)); - #endif - if( SD.rename( buf, path )) - client.println( "250 File successfully renamed or moved"); - else - client.println( "451 Rename/move failure"); - - } - }*/ - rnfrCmd = false; - } - - /////////////////////////////////////// - // // - // EXTENSIONS COMMANDS (RFC 3659) // - // // - /////////////////////////////////////// - - // - // FEAT - New Features - // - else if( ! strcmp( command, "FEAT" )) - { - client.println( "211-Extensions suported:"); - client.println( " MLSD"); - client.println( "211 End."); - } - // - // MDTM - File Modification Time (see RFC 3659) - // - else if (!strcmp(command, "MDTM")) - { - client.println("550 Unable to retrieve time"); - } - - // - // SIZE - Size of the file - // - else if( ! strcmp( command, "SIZE" )) - { - char path[ FTP_CWD_SIZE ]; - if( strlen( parameters ) == 0 ) - client.println( "501 No file name"); - else if( makePath( path )) - { - file = SD.open(path, FILE_READ); - if(!file) - client.println( "450 Can't open " +String(parameters) ); - else - { - client.println( "213 " + String(file.size())); - file.close(); - } - } - } - // - // SITE - System command - // - else if( ! strcmp( command, "SITE" )) - { - client.println( "500 Unknow SITE command " +String(parameters) ); - } - // - // Unrecognized commands ... - // - else - client.println( "500 Unknow command"); - - return true; -} - -boolean FtpServer::dataConnect() -{ - unsigned long startTime = millis(); - //wait 5 seconds for a data connection - if (!data.connected()) - { - while (!dataServer.hasClient() && millis() - startTime < 10000) - { - //delay(100); - yield(); - } - if (dataServer.hasClient()) { - data.stop(); - data = dataServer.available(); - #ifdef FTP_DEBUG - Serial.println("ftpdataserver client...."); - #endif - - } - } - - return data.connected(); - -} - -boolean FtpServer::doRetrieve() -{ -if (data.connected()) -{ - int16_t nb = file.readBytes(buf, FTP_BUF_SIZE); - if (nb > 0) - { - data.write((uint8_t*)buf, nb); - bytesTransfered += nb; - return true; - } -} -closeTransfer(); -return false; -} - -boolean FtpServer::doStore() -{ - // Avoid blocking by never reading more bytes than are available - int navail = data.available(); - - if (navail > 0) - { - // And be sure not to overflow buf. - if (navail > FTP_BUF_SIZE) navail = FTP_BUF_SIZE; - int16_t nb = data.read((uint8_t*) buf, navail ); - // int16_t nb = data.readBytes((uint8_t*) buf, FTP_BUF_SIZE ); - if( nb > 0 ) - { - // Serial.println( millis() << " " << nb << endl; - file.write((uint8_t*) buf, nb ); - bytesTransfered += nb; - } - } - if( !data.connected() && (navail <= 0) ) - { - closeTransfer(); - return false; - } - else - { - return true; - } -} - -void FtpServer::closeTransfer() -{ - uint32_t deltaT = (int32_t) ( millis() - millisBeginTrans ); - if( deltaT > 0 && bytesTransfered > 0 ) - { - client.println( "226-File successfully transferred"); - client.println( "226 " + String(deltaT) + " ms, "+ String(bytesTransfered / deltaT) + " kbytes/s"); - } - else - client.println( "226 File successfully transferred"); - - file.close(); - data.stop(); -} - -void FtpServer::abortTransfer() -{ - if( transferStatus > 0 ) - { - file.close(); - data.stop(); - client.println( "426 Transfer aborted" ); - #ifdef FTP_DEBUG - Serial.println( "Transfer aborted!") ; - #endif - } - transferStatus = 0; -} - -// Read a char from client connected to ftp server -// -// update cmdLine and command buffers, iCL and parameters pointers -// -// return: -// -2 if buffer cmdLine is full -// -1 if line not completed -// 0 if empty line received -// length of cmdLine (positive) if no empty line received - -int8_t FtpServer::readChar() -{ - int8_t rc = -1; - - if( client.available()) - { - char c = client.read(); - // char c; - // client.readBytes((uint8_t*) c, 1); - #ifdef FTP_DEBUG - Serial.print( c); - #endif - if( c == '\\' ) - c = '/'; - if( c != '\r' ) - if( c != '\n' ) - { - if( iCL < FTP_CMD_SIZE ) - cmdLine[ iCL ++ ] = c; - else - rc = -2; // Line too long - } - else - { - cmdLine[ iCL ] = 0; - command[ 0 ] = 0; - parameters = NULL; - // empty line? - if( iCL == 0 ) - rc = 0; - else - { - rc = iCL; - // search for space between command and parameters - parameters = strchr( cmdLine, ' ' ); - if( parameters != NULL ) - { - if( parameters - cmdLine > 4 ) - rc = -2; // Syntax error - else - { - strncpy( command, cmdLine, parameters - cmdLine ); - command[ parameters - cmdLine ] = 0; - - while( * ( ++ parameters ) == ' ' ) - ; - } - } - else if( strlen( cmdLine ) > 4 ) - rc = -2; // Syntax error. - else - strcpy( command, cmdLine ); - iCL = 0; - } - } - if( rc > 0 ) - for( uint8_t i = 0 ; i < strlen( command ); i ++ ) - command[ i ] = toupper( command[ i ] ); - if( rc == -2 ) - { - iCL = 0; - client.println( "500 Syntax error"); - } - } - return rc; -} - -// Make complete path/name from cwdName and parameters -// -// 3 possible cases: parameters can be absolute path, relative path or only the name -// -// parameters: -// fullName : where to store the path/name -// -// return: -// true, if done - -boolean FtpServer::makePath( char * fullName ) -{ - return makePath( fullName, parameters ); -} - -boolean FtpServer::makePath( char * fullName, char * param ) -{ - if( param == NULL ) - param = parameters; - - // Root or empty? - if( strcmp( param, "/" ) == 0 || strlen( param ) == 0 ) - { - strcpy( fullName, "/" ); - return true; - } - // If relative path, concatenate with current dir - if( param[0] != '/' ) - { - strcpy( fullName, cwdName ); - if( fullName[ strlen( fullName ) - 1 ] != '/' ) - strncat( fullName, "/", FTP_CWD_SIZE ); - strncat( fullName, param, FTP_CWD_SIZE ); - } - else - strcpy( fullName, param ); - // If ends with '/', remove it - uint16_t strl = strlen( fullName ) - 1; - if( fullName[ strl ] == '/' && strl > 1 ) - fullName[ strl ] = 0; - if( strlen( fullName ) < FTP_CWD_SIZE ) - return true; - - client.println( "500 Command line too long"); - return false; -} - -// Calculate year, month, day, hour, minute and second -// from first parameter sent by MDTM command (YYYYMMDDHHMMSS) -// -// parameters: -// pyear, pmonth, pday, phour, pminute and psecond: pointer of -// variables where to store data -// -// return: -// 0 if parameter is not YYYYMMDDHHMMSS -// length of parameter + space - -uint8_t FtpServer::getDateTime( uint16_t * pyear, uint8_t * pmonth, uint8_t * pday, - uint8_t * phour, uint8_t * pminute, uint8_t * psecond ) -{ - char dt[ 15 ]; - - // Date/time are expressed as a 14 digits long string - // terminated by a space and followed by name of file - if( strlen( parameters ) < 15 || parameters[ 14 ] != ' ' ) - return 0; - for( uint8_t i = 0; i < 14; i++ ) - if( ! isdigit( parameters[ i ])) - return 0; - - strncpy( dt, parameters, 14 ); - dt[ 14 ] = 0; - * psecond = atoi( dt + 12 ); - dt[ 12 ] = 0; - * pminute = atoi( dt + 10 ); - dt[ 10 ] = 0; - * phour = atoi( dt + 8 ); - dt[ 8 ] = 0; - * pday = atoi( dt + 6 ); - dt[ 6 ] = 0 ; - * pmonth = atoi( dt + 4 ); - dt[ 4 ] = 0 ; - * pyear = atoi( dt ); - return 15; -} - -// Create string YYYYMMDDHHMMSS from date and time -// -// parameters: -// date, time -// tstr: where to store the string. Must be at least 15 characters long -// -// return: -// pointer to tstr - -char * FtpServer::makeDateTimeStr( char * tstr, uint16_t date, uint16_t time ) -{ - sprintf( tstr, "%04u%02u%02u%02u%02u%02u", - (( date & 0xFE00 ) >> 9 ) + 1980, ( date & 0x01E0 ) >> 5, date & 0x001F, - ( time & 0xF800 ) >> 11, ( time & 0x07E0 ) >> 5, ( time & 0x001F ) << 1 ); - return tstr; -} - diff --git a/lib/esp8266FTPServer/ESP8266FtpServer.h b/lib/esp8266FTPServer/ESP8266FtpServer.h deleted file mode 100644 index 687a937..0000000 --- a/lib/esp8266FTPServer/ESP8266FtpServer.h +++ /dev/null @@ -1,108 +0,0 @@ - -/* -* FTP SERVER FOR ESP8266 - * based on FTP Serveur for Arduino Due and Ethernet shield (W5100) or WIZ820io (W5200) - * based on Jean-Michel Gallego's work - * modified to work with esp8266 SPIFFS by David Paiva (david@nailbuster.com) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -/******************************************************************************* - ** ** - ** DEFINITIONS FOR FTP SERVER ** - ** ** - *******************************************************************************/ - -// Uncomment to print debugging info to console attached to ESP8266 -//#define FTP_DEBUG - -#ifndef FTP_SERVERESP_H -#define FTP_SERVERESP_H - -#define FTP_DEBUG - -//#include "Streaming.h" -#include -#include - -#define FTP_SERVER_VERSION "FTP-2017-10-18" - -#define FTP_CTRL_PORT 21 // Command port on wich server is listening -#define FTP_DATA_PORT_PASV 50009 // Data port in passive mode - -#define FTP_TIME_OUT 5 // Disconnect client after 5 minutes of inactivity -#define FTP_CMD_SIZE 255 + 8 // max size of a command -#define FTP_CWD_SIZE 255 + 8 // max size of a directory name -#define FTP_FIL_SIZE 255 // max size of a file name -//#define FTP_BUF_SIZE 1024 //512 // size of file buffer for read/write -#define FTP_BUF_SIZE 2*1460 //512 // size of file buffer for read/write - -class FtpServer -{ -public: - void begin(String uname, String pword); - void handleFTP(); - -private: - void iniVariables(); - void clientConnected(); - void disconnectClient(); - boolean userIdentity(); - boolean userPassword(); - boolean processCommand(); - boolean dataConnect(); - boolean doRetrieve(); - boolean doStore(); - void closeTransfer(); - void abortTransfer(); - boolean makePath( char * fullname ); - boolean makePath( char * fullName, char * param ); - uint8_t getDateTime( uint16_t * pyear, uint8_t * pmonth, uint8_t * pday, - uint8_t * phour, uint8_t * pminute, uint8_t * second ); - char * makeDateTimeStr( char * tstr, uint16_t date, uint16_t time ); - int8_t readChar(); - - IPAddress dataIp; // IP address of client for data - WiFiClient client; - WiFiClient data; - - File file; - - boolean dataPassiveConn; - uint16_t dataPort; - char buf[ FTP_BUF_SIZE ]; // data buffer for transfers - char cmdLine[ FTP_CMD_SIZE ]; // where to store incoming char from client - char cwdName[ FTP_CWD_SIZE ]; // name of current directory - char command[ 5 ]; // command sent by client - boolean rnfrCmd; // previous command was RNFR - char * parameters; // point to begin of parameters sent by client - uint16_t iCL; // pointer to cmdLine next incoming char - int8_t cmdStatus, // status of ftp command connexion - transferStatus; // status of ftp data transfer - uint32_t millisTimeOut, // disconnect after 5 min of inactivity - millisDelay, - millisEndConnection, // - millisBeginTrans, // store time of beginning of a transaction - bytesTransfered; // - String _FTP_USER; - String _FTP_PASS; - - - -}; - -#endif // FTP_SERVERESP_H - - diff --git a/lib/esp8266FTPServer/LICENSE b/lib/esp8266FTPServer/LICENSE deleted file mode 100644 index 19e3071..0000000 --- a/lib/esp8266FTPServer/LICENSE +++ /dev/null @@ -1,504 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -(This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.) - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - {description} - Copyright (C) {year} {fullname} - - 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 Street, Fifth Floor, Boston, MA 02110-1301 - USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random - Hacker. - - {signature of Ty Coon}, 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! diff --git a/lib/esp8266FTPServer/README.md b/lib/esp8266FTPServer/README.md deleted file mode 100644 index dd9bc01..0000000 --- a/lib/esp8266FTPServer/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# esp8266FTPServer -Simple FTP Server for using esp8266/esp32 SPIFFs - -Now should support esp32!!! - -I've modified a FTP server from arduino/wifi shield to work with esp8266.... - -This allows you to FTP into your esp8266 and access/modify the spiffs folder/data...it only allows one ftp connection at a time....very simple for now... - -I've tested it with Filezilla, and the basics work (upload/download/rename/delete). There's no create/modify directory support(no directory support in SPIFFS yet). - -You need to setup Filezilla(or other client) to only allow 1 connection.. -To force FileZilla to use the primary connection for data transfers: -Go to File/Site Manager then select your site. -In Transfer Settings, check "Limit number of simultaneous connections" and set the maximum to 1 - -only supports Passive ftp mode.... - -It does NOT support any encryption, so you'll have to disable any form of encryption... - -feel free to try it out (sample provided)....unzip into your arduino library directory (and restart arduino ide). - - -this is the original project on github I worked from: https://github.com/gallegojm/Arduino-Ftp-Server/tree/master/FtpServer diff --git a/lib/esp8266FTPServer/library.json b/lib/esp8266FTPServer/library.json deleted file mode 100644 index ff9891f..0000000 --- a/lib/esp8266FTPServer/library.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "esp8266FTPServer", - "description": "Simple FTP Server for using esp8266 SPIFFs", - "keywords": "esp8266, ftp, spiffs", - "authors": - { - "name": "David Paiva", - "email": "david@paivahome.com", - "url": "http://nailbuster.com/" - }, - "repository": - { - "type": "git", - "url": "https://github.com/nailbuster/esp8266FTPServer" - }, - "url": "http://nailbuster.com/", - "frameworks": "Arduino", - "platforms": "*" -} diff --git a/lib/esp8266FTPServer/library.properties b/lib/esp8266FTPServer/library.properties deleted file mode 100644 index 0e89838..0000000 --- a/lib/esp8266FTPServer/library.properties +++ /dev/null @@ -1,9 +0,0 @@ -name=ESP8266FtpServer -version=1.0.1 -author= -maintainer=david@paivahome.com -sentence=Very Simple FTP server for SPIFFS on esp8266 -paragraph=Very Simple FTP server for SPIFFS on esp8266 -category=Communication -url= -architectures=esp8266,esp32,arduino-esp32 diff --git a/platformio.ini b/platformio.ini index 75c1281..4bfcf48 100644 --- a/platformio.ini +++ b/platformio.ini @@ -15,6 +15,8 @@ framework = arduino upload_speed = 512000 build_flags=!./build_version.sh lib_deps = MFRC522 + https://github.com/me-no-dev/ESPAsyncWebServer.git + ArduinoJSON upload_port = /dev/cu.SLAB_USBtoUART monitor_speed = 74480 ;monitor_port = /dev/cu.wchusbserial1420 \ No newline at end of file diff --git a/src/controller.cpp b/src/controller.cpp index 7b570b9..178b40c 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -2,11 +2,15 @@ #include "spi_master.h" #include "config.h" #include "playlist.h" +#include "http_server.h" +#include Controller::Controller(Player* p, PlaylistManager* pm) { _player = p; _pm = pm; _rfid = new MFRC522(17, MFRC522::UNUSED_PIN); + + _player->register_controller(this); BTN_NEXT_SETUP(); BTN_PREV_SETUP(); @@ -29,19 +33,28 @@ void Controller::set_mqtt_client(MQTTClient* m) { _mqtt_client = m; } +void Controller::register_http_server(HTTPServer* h) { + _http_server = h; +} + void Controller::loop() { + TRACE("Controller::loop()...\n"); 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 ((_last_mqtt_report_at < now - MQTT_REPORT_INTERVAL) || (now < _last_mqtt_report_at)) { - _send_mqtt_report(); - _last_mqtt_report_at = now; + if (_cmd_queue.length() > 0) { + process_message(_cmd_queue); + _cmd_queue = ""; } + TRACE("Controller::loop() done.\n"); } uint32_t Controller::_get_rfid_card_uid() { @@ -60,6 +73,7 @@ uint32_t Controller::_get_rfid_card_uid() { } void Controller::_check_rfid() { + TRACE("check_rfid running...\n"); MFRC522::StatusCode status; if (_rfid_present) { byte buffer[2]; @@ -91,9 +105,11 @@ void Controller::_check_rfid() { } 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) { @@ -110,7 +126,15 @@ void Controller::_check_rfid() { return; } int index; - if (data.indexOf("[random]") != -1 && pl->is_fresh()) { + 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 { + // TODO + } + } 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); @@ -133,11 +157,13 @@ void Controller::_check_rfid() { } _player->play(pl); + send_playlist_manager_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 @@ -205,12 +231,14 @@ String Controller::_read_rfid_data() { } void Controller::_check_serial() { + TRACE("check_serial running...\n"); + if (Serial.available() > 0) { char c = Serial.read(); Serial.printf("%c", c); if (c==10 || c==13) { if (_serial_buffer.length()>0) { - _execute_serial_command(_serial_buffer); + process_message(_serial_buffer); _serial_buffer = String(); } } else { @@ -219,18 +247,19 @@ void Controller::_check_serial() { } } -void Controller::_execute_serial_command(String cmd) { +bool Controller::process_message(String cmd) { DEBUG("Executing command: %s\n", cmd.c_str()); if (cmd.startsWith("play ")) { - Playlist* p = _pm->get_playlist_for_id(cmd.substring(5)); + Playlist* p = _pm->get_playlist_for_folder(cmd.substring(5)); _player->play(p); //} else if (cmd.equals("ls")) { // _execute_command_ls("/"); //} else if (cmd.startsWith("ls ")) { // _execute_command_ls(cmd.substring(3)); - //} else if (cmd.equals("play")) { - // _player->play_random_album(); + } else if (cmd.equals("play")) { + _player->play(); + } else if (cmd.equals("stop")) { _player->stop(); } else if (cmd.equals("help")) { @@ -239,16 +268,28 @@ void Controller::_execute_serial_command(String cmd) { _player->vol_down(); } else if (cmd.equals("+")) { _player->vol_up(); - } else if (cmd.equals("p")) { + } 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("n")) { + } 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 { ERROR("Unknown command: %s\n", cmd.c_str()); + return false; } - return; + return true; } void Controller::_execute_command_ls(String path) { @@ -272,6 +313,8 @@ void Controller::_execute_command_help() { } void Controller::_check_buttons() { + TRACE("check_buttons running...\n"); + if (BTN_PREV() && _debounce_button(0)) { if (_state == NORMAL) { _player->track_prev(); @@ -300,33 +343,55 @@ bool Controller::_debounce_button(uint8_t index) { _button_last_pressed_at[index] = millis(); return ret; } -String Controller::get_status_json() { - String response = String("{"); - response.concat("\"state\": \""); - response.concat(_player->is_playing() ? "playing" : "idle"); - response.concat("\", "); - if (_player->is_playing()) { - // TODO - //response.concat("\"album\": \""); - //response.concat(_player->album()); - //response.concat("\", \"track\": "); - //response.concat(_player->track()); - //response.concat(", \"position\": "); - //response.concat(_player->position()); - //response.concat(", "); + +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; } - response.concat("\"volume\": "); - response.concat(_player->volume()); - response.concat(", \"volume_max\": "); - response.concat(VOLUME_MAX); - response.concat(", \"volume_min\": "); - response.concat(VOLUME_MIN); - response.concat(", \"rfid_uid\": "); - response.concat(String(_last_rfid_card_uid, HEX)); - response.concat("}\n"); - return response; + JsonObject rfid = json.createNestedObject("last_rfid"); + rfid["uid"] = _last_rfid_uid; + rfid["data"] = _last_rfid_data; + return json.as(); } -void Controller::_send_mqtt_report() { +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::inform_new_client(AsyncWebSocketClient* client) { + String s; + s += _pm->json(); + s += '\n'; + s += _player->json(); + s += '\n'; + s += _player->position_json(); + client->text(s); +} + +void Controller::queue_command(String s) { + _cmd_queue = s; +} \ No newline at end of file diff --git a/src/http_server.cpp b/src/http_server.cpp index 40372c7..9697576 100644 --- a/src/http_server.cpp +++ b/src/http_server.cpp @@ -1,32 +1,23 @@ -/* #include "http_server.h" +#include HTTPServer::HTTPServer(Player* p, Controller* c) { _player = p; _controller = c; - _http_server = new ESP8266WebServer(80); - - //_http_server->onFileUpload([&]() { _handle_upload(); yield();}); - _http_server->on("/upload", HTTP_POST, [&]() { - _http_server->sendHeader("Connection", "close"); - _http_server->send(200, "text/plain", "OK"); - }, [&]() { - _handle_upload(); - yield(); - }); - _http_server->on("/", HTTP_GET, [&](){ _handle_index(); }); - _http_server->on("/status", HTTP_GET, [&](){ _handle_status(); }); - _http_server->begin(); + _server = new AsyncWebServer(80); + ws = new AsyncWebSocket("/ws"); + _server->addHandler(ws); + ws->onEvent([&](AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){this->_onEvent(server, client, type, arg, data, len);}); + _server->on("/", [&](AsyncWebServerRequest* req) {_handle_index(req);}); + _server->begin(); MDNS.addService("http", "tcp", 80); } -void HTTPServer::_handle_upload() { +void HTTPServer::_handle_upload(AsyncWebServerRequest* request, String filename, size_t index, uint8_t* data, size_t len, bool final) { // https://www.gnu.org/software/tar/manual/html_node/Standard.html // https://www.mkssoftware.com/docs/man4/tar.4.asp - HTTPUpload* upload = &_http_server->upload(); - DEBUG("_handle_upload Status: %d, length: %d\n", upload->status, upload->currentSize); - if (upload->status == UPLOAD_FILE_START) { + if (index == 0) { // Starting upload _chunk = new uint8_t[512]; _chunk_length = 0; _upload_position = 0; @@ -35,19 +26,13 @@ void HTTPServer::_handle_upload() { _need_header = true; } - if (upload->status == UPLOAD_FILE_END || upload->status == UPLOAD_FILE_ABORTED) { - // Close the file - delete _chunk; - return; - } - uint32_t upload_offset = 0; - while (upload_offset < upload->currentSize) { + while (upload_offset < len) { // Load a chunk - if (_chunk_length < 512 && upload->currentSize > upload_offset) { + if (_chunk_length < 512 && len > upload_offset) { uint16_t needed = 512 - _chunk_length; - if (needed > upload->currentSize - upload_offset) needed = upload->currentSize - upload_offset; - memcpy(_chunk + _chunk_length, upload->buf + upload_offset, needed); + if (needed > len - upload_offset) needed = len - upload_offset; + memcpy(_chunk + _chunk_length, data + upload_offset, needed); _chunk_length += needed; upload_offset += needed; _upload_position += needed; @@ -97,29 +82,30 @@ void HTTPServer::_handle_upload() { _chunk_length = 0; } } - + } + + if (final == true) { + // Close the file + delete _chunk; + return; } } -void HTTPServer::_handle_index() { - String response = String("ESMP3"); - response.concat("Albums on SD card:"); - std::list files = _player->ls("/", false, true, false); - for(std::list::iterator it=files.begin(); it!=files.end(); it++) { - response.concat("\n"); +void HTTPServer::_onEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) { + if (type==WS_EVT_CONNECT) { + _controller->inform_new_client(client); + } else if (type==WS_EVT_DATA) { + AwsFrameInfo* info = (AwsFrameInfo*) arg; + if (info->final && info->index==0 && info->len==len && info->opcode==WS_TEXT) { + DEBUG("Received ws message: %s\n", (char*)data); + _controller->queue_command((char*)data); + } } - response.concat("
"); - response.concat(*it); - response.concat("Play
"); - _http_server->send(200, "text/html", response); } -void HTTPServer::_handle_status() { - _http_server->send(200, "application/json", _controller->get_status_json()); -} +void HTTPServer::_handle_index(AsyncWebServerRequest* r) { -void HTTPServer::loop() { - _http_server->handleClient(); - MDNS.update(); -} -*/ \ No newline at end of file +#include "index.html" + + r->send(200, "text/html", html); +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 0b301ab..97fc322 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include "config.h" #include "controller.h" #include "player.h" @@ -9,14 +10,13 @@ #include "http_server.h" #include "mqtt_client.h" #include "playlist_manager.h" -#include Controller* controller; Player* player; PlaylistManager* pm; -//HTTPServer* http_server; -FtpServer* ftp_server; +HTTPServer* http_server; MQTTClient* mqtt_client; + unsigned long last_mqtt_report = 0; void setup() { @@ -65,18 +65,28 @@ void setup() { delay(1000); ESP.restart(); } - INFO("WiFi connected.\n"); + INFO("WiFi connected. IP address: %s\n", WiFi.localIP().toString().c_str()); mqtt_client = new MQTTClient(); - //MDNS.begin("esmp3"); + MDNS.begin("esmp3"); controller->set_mqtt_client(mqtt_client); - DEBUG("Setting up WiFi and web server...\n"); - //http_server = new HTTPServer(player, controller); + DEBUG("Setting up HTTP server...\n"); + http_server = new HTTPServer(player, controller); + controller->register_http_server(http_server); - ftp_server = new FtpServer(); - ftp_server->begin("user", "pass"); + DEBUG("Starting NTP client...\n"); + // Taken from https://github.com/esp8266/Arduino/blob/master/cores/esp8266/TZ.h + configTzTime("CET-1CEST,M3.5.0,M10.5.0/3", "europe.pool.ntp.org"); + struct tm time; + if (getLocalTime(&time, 10000)) { + char buffer[100]; + strftime(buffer, 100, "%Y-%m-%d %H:%M:%S", &time); + DEBUG("Got time: %s\n", buffer); + } else { + INFO("Could not fetch current time via NTP.\n"); + } INFO("Initialization completed.\n"); } @@ -86,11 +96,9 @@ void loop() { if (more_data_needed) return; controller->loop(); - //http_server->loop(); - ftp_server->handleFTP(); mqtt_client->loop(); if ((last_mqtt_report + 10000 < millis()) || last_mqtt_report > millis()) { last_mqtt_report = millis(); - mqtt_client->publish_status(controller->get_status_json()); + mqtt_client->publish_status(controller->json()); } } diff --git a/src/player.cpp b/src/player.cpp index eec0b5a..3fb9ec7 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -2,6 +2,7 @@ #include "player.h" #include "spi_master.h" +#include //Player::_spi_settings @@ -15,7 +16,11 @@ Player::Player(SPIMaster* s) { _spi->disable(); PIN_VS1053_DREQ_SETUP(); - _init(); + init(); +} + +void Player::register_controller(Controller* c) { + _controller = c; } void Player::_reset() { @@ -27,7 +32,7 @@ void Player::_reset() { _spi_settings = &_spi_settings_slow; // After reset, communication has to be slow } -void Player::_init() { +void Player::init() { DEBUG("Resetting VS1053...\n"); _reset(); @@ -91,6 +96,7 @@ void Player::_sleep() { _write_control_register(SCI_AUDATA, 0x0010); set_volume(0, false); _state = sleeping; + TRACE("VS1053 is sleeping now.\n"); } void Player::_wakeup() { @@ -506,6 +512,12 @@ void Player::track_prev() { } } +void Player::set_track(uint8_t id) { + stop(); + _current_playlist->set_track(id); + play(); +} + bool Player::is_playing() { return _state == playing; } @@ -518,10 +530,13 @@ bool Player::play(Playlist* p) { bool Player::play() { if (_state == sleeping || _state == recording) _wakeup(); if (_state != idle) return false; + if (_current_playlist == NULL) return false; + _current_playlist->start(); String file = _current_playlist->get_current_file(); uint32_t position = _current_playlist->get_position(); _state = playing; _play_file(file, position); + _controller->send_player_status(); return true; } @@ -529,6 +544,7 @@ void Player::_play_file(String file, uint32_t file_offset) { INFO("play_file('%s', %d)\n", file.c_str(), file_offset); _spi->select_sd(); _file = SD.open(file); + _file_size = _file.size(); _spi->select_sd(false); if (!_file) { DEBUG("Could not open file %s", file.c_str()); @@ -552,6 +568,7 @@ void Player::_play_file(String file, uint32_t file_offset) { if (_skip_to>0) _mute(); else _speaker_on(); INFO("Now playing.\n"); + _controller->send_player_status(); } uint32_t Player::_id3_tag_offset(File f) { @@ -601,7 +618,7 @@ void Player::_finish_playing() { // If we reached this, the Chip didn't stop. That should not happen. // (That's written in the manual.) // Reset the chip. - _init(); + init(); } void Player::stop(bool turn_speaker_off) { @@ -635,7 +652,10 @@ void Player::_finish_stopping(bool turn_speaker_off) { if (_file) { _file.close(); } + _current_play_position = 0; + _file_size = 0; INFO("Stopped.\n"); + _controller->send_player_status(); } void Player::_refill() { @@ -655,7 +675,9 @@ void Player::_refill() { play(); } else { _current_playlist->reset(); + _controller->send_player_status(); } + return; } _current_play_position+=result; @@ -672,6 +694,7 @@ void Player::_refill() { _spi->select_sd(false); _skip_to = 0; _unmute(); + _controller->send_position(); } } else { _skip_to = 0; @@ -712,3 +735,29 @@ bool Player::loop() { } return false; } + +String Player::json() { + DynamicJsonDocument json(10240); + json["_type"] = "player"; + json["playing"] = is_playing(); + if (_current_playlist) { + JsonObject playlist = json.createNestedObject("playlist"); + _current_playlist->json(playlist); + } else { + json["playlist"] = nullptr; + } + JsonObject volume = json.createNestedObject("volume"); + volume["current"] = _volume; + volume["min"] = VOLUME_MIN; + volume["max"] = VOLUME_MAX; + volume["step"] = VOLUME_STEP; + return json.as(); +} + +String Player::position_json() { + DynamicJsonDocument json(200); + json["_type"] = "position"; + json["position"] = _current_play_position; + json["file_size"] = _file_size; + return json.as(); +} diff --git a/src/playlist.cpp b/src/playlist.cpp index f089df9..67ca71f 100644 --- a/src/playlist.cpp +++ b/src/playlist.cpp @@ -3,6 +3,7 @@ #include "config.h" #include #include +#include Playlist::Playlist(String path) { // Add files to _files @@ -29,6 +30,17 @@ Playlist::Playlist(String path) { ext.equals(".mpa"))) { TRACE(" Adding entry %s\n", entry.name()); _files.push_back(entry.name()); + bool non_ascii_chars = false; + for(int i=0; i= 0x7F) { + non_ascii_chars = true; + break; + } + } + if (non_ascii_chars) { + ERROR("WARNING: File '%s' contains non-ascii chars!\n", filename.c_str()); + } } else { TRACE(" Ignoring entry %s\n", filename.c_str()); } @@ -39,6 +51,10 @@ Playlist::Playlist(String path) { std::sort(_files.begin(), _files.end()); } +void Playlist::start() { + _started = true; +} + bool Playlist::has_track_prev() { return _current_track > 0; } @@ -65,6 +81,14 @@ bool Playlist::track_next() { return false; } +bool Playlist::set_track(uint8_t track) { + if (track < _files.size()) { + _current_track = track; + return true; + } + return false; +} + void Playlist::track_restart() { _position = 0; } @@ -84,11 +108,21 @@ void Playlist::shuffle(uint8_t random_offset) { TRACE("Done.\n"); } +void Playlist::advent_shuffle(uint8_t day) { + if (day > 24) day = 24; + + if (day > _files.size()) return; + + _files.insert(_files.begin(), _files[day - 1]); + _files.erase(_files.begin() + day - 1, _files.end()); +} + void Playlist::reset() { std::sort(_files.begin(), _files.end()); _current_track = 0; _position = 0; _shuffled = false; + _started = false; } String Playlist::get_current_file() { @@ -104,5 +138,22 @@ void Playlist::set_position(uint32_t p) { } bool Playlist::is_fresh() { - return !_shuffled && _position==0 && _current_track==0; -} \ No newline at end of file + return !_shuffled && !_started && _position==0 && _current_track==0; +} + +void Playlist::dump() { + for (int i=0; i<_files.size(); i++) { + DEBUG(" %02d %2s %s\n", i+1, (i==_current_track) ? "->" : "", _files[i].c_str()); + } +} + +void Playlist::json(JsonObject json) { + json["_type"] = "playlist"; + JsonArray files = json.createNestedArray("files"); + for (String file: _files) { + files.add(file); + } + json["current_track"] = _current_track; + json["has_track_next"] = has_track_next(); + json["has_track_prev"] = has_track_prev(); +} diff --git a/src/playlist_manager.cpp b/src/playlist_manager.cpp index 9c04c20..e962583 100644 --- a/src/playlist_manager.cpp +++ b/src/playlist_manager.cpp @@ -1,6 +1,7 @@ #include "playlist_manager.h" #include #include "spi_master.h" +#include PlaylistManager::PlaylistManager() { SPIMaster::select_sd(); @@ -8,12 +9,40 @@ PlaylistManager::PlaylistManager() { File entry; while (entry = root.openNextFile()) { String foldername = entry.name(); + if (!entry.isDirectory() || foldername.startsWith("/.")) continue; // Remove trailing slash foldername.remove(foldername.length()); - TRACE("Looking at %s...", foldername.c_str()); - if (!entry.isDirectory() || foldername.startsWith("/.")) continue; + DEBUG(" Checking %s...\n", foldername.c_str()); + bool non_ascii_chars = false; + for(int i=0; i= 0x7F) { + non_ascii_chars = true; + break; + } + } + if (non_ascii_chars) { + ERROR("WARNING: Folder '%s' contains non-ascii chars!\n", foldername.c_str()); + } + + if (!SD.exists(foldername + "/ids.txt")) { - TRACE("No ids.txt -> ignoring\n"); + TRACE("No ids.txt -> checking for media files...\n"); + + File file; + bool media_files = false; + while(file = entry.openNextFile()) { + String filename = file.name(); + filename = filename.substring(foldername.length() + 1); + if (!filename.startsWith(".") && filename.endsWith(".mp3")) { + media_files = true; + } + file.close(); + if (media_files) break; + } + if (media_files) { + _unmapped_folders.push_back(foldername); + } continue; } File f = SD.open(foldername + "/ids.txt"); @@ -47,6 +76,10 @@ PlaylistManager::PlaylistManager() { Playlist* PlaylistManager::get_playlist_for_id(String id) { if (!_map.count(id)) return NULL; String folder = _map[id]; + return get_playlist_for_folder(folder); +} + +Playlist* PlaylistManager::get_playlist_for_folder(String folder) { if (!_playlists.count(folder)) { _playlists[folder] = new Playlist(folder); } @@ -58,3 +91,22 @@ void PlaylistManager::dump_ids() { INFO(" %s -> %s\n", it->first.c_str(), it->second.c_str()); } } + +String PlaylistManager::json() { + DynamicJsonDocument json(10240); + json["_type"] = "playlist_manager"; + JsonObject folders = json.createNestedObject("folders"); + for (std::map::iterator it = _map.begin(); it!=_map.end(); it++) { + folders[it->second] = folders[it->first]; + } + JsonArray started = json.createNestedArray("started"); + for (std::map::iterator it = _playlists.begin(); it!=_playlists.end(); it++) { + if (it->second->is_fresh()) continue; + started.add(it->first); + } + JsonArray unmapped = json.createNestedArray("unmapped"); + for(String folder : _unmapped_folders) { + unmapped.add(folder); + } + return json.as(); +}