Skip to content

Instantly share code, notes, and snippets.

@2200913
Last active October 2, 2023 23:44
Show Gist options
  • Save 2200913/21e7624603863b5a339a98b6594404cb to your computer and use it in GitHub Desktop.
Save 2200913/21e7624603863b5a339a98b6594404cb to your computer and use it in GitHub Desktop.
Sistemas Computacionais Embebidos, projeto melhoria
/*
* 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 <http://www.gnu.org/licenses/>.
*/
// 2017: modified by @robo8080
// 2019: modified by @HenrikSte
#include "ESP32FtpServer.h"
#include <WiFi.h>
#include <WiFiClient.h>
//#include <ESP32WebServer.h>
#include <FS.h>
//#include "SD.h"
#include "SD.h"
#include "SPI.h"
#define FTP_DEBUG
WiFiServer ftpServer( FTP_CTRL_PORT );
WiFiServer dataServer( FTP_DATA_PORT_PASV );
FtpServer::FtpServer()
{
}
void FtpServer::begin(String uname, String pword)
{
// Tells the ftp server to begin listening for incoming connection
_FTP_USER=uname;
_FTP_PASS = pword;
if(!SD.begin())
{
Serial.println("Card Mount Failed");
return;
}
uint8_t cardType = SD.cardType();
if(cardType == CARD_NONE){
Serial.println("No SD card attached");
return;
}
Serial.print("SD Card Type: ");
if(cardType == CARD_MMC){
Serial.println("MMC");
} else if(cardType == CARD_SD){
Serial.println("SDSC");
} else if(cardType == CARD_SDHC){
Serial.println("SDHC");
} else {
Serial.println("UNKNOWN");
}
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;
}
int FtpServer::handleFTP()
{
if((int32_t) ( millisDelay - millis() ) > 0 )
return 0;
if (ftpServer.hasClient()) {
// if (ftpServer.available()) {
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;
}
return transferStatus!=0
|| cmdStatus !=0;
}
void FtpServer::clientConnected()
{
#ifdef FTP_DEBUG
Serial.println("Client connected!");
#endif
client.println( "220--- Welcome to FTP for 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" ))
{
int todo;
client.println("250 Ok. Current directory is \"" + String(cwdName) + "\"");
}
//
// CWD - Change Working Directory
//
else if( ! strcmp( command, "CWD" ))
{
if( strcmp( parameters, "." ) == 0 ) // 'CWD .' is the same as PWD command
client.println( "257 \"" + String(cwdName) + "\" is your current directory");
else
{
#ifdef FTP_DEBUG
Serial.print("CWD P=");
Serial.print(parameters);
Serial.print(" CWD=");
Serial.println(cwdName);
#endif
String dir;
if (parameters[0]=='/')
{
dir = parameters;
}
else if (!strcmp(cwdName,"/")) // avoid "\\newdir"
{
dir = String("/") + parameters;
}
else
{
dir = String(cwdName) +"/" + parameters;
}
if (SD.exists(dir))
{
strcpy(cwdName, dir.c_str());
client.println( "250 CWD Ok. Current directory is \"" + String(dir) + "\"");
Serial.println( "250 CWD Ok. Current directory is \"" + String(dir) + "\"");
}
else
{
client.println( "550 directory or file does not exist \"" + String(parameters) + "\"");
Serial.println( "550 directory or file does not exist \"" + String(parameters) + "\"");
}
}
}
//
// 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 = WiFi.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;
// Dir dir=SD.openDir(cwdName);
File dir=SD.open(cwdName);
// if( !SD.exists(cwdName))
if((!dir)||(!dir.isDirectory()))
client.println( "550 Can't open directory " + String(cwdName) );
else
{
File file = dir.openNextFile();
while( file)
{
String fn, fs;
fn = file.name();
fn.remove(0, 1);
#ifdef FTP_DEBUG
Serial.println("File Name = "+ fn);
#endif
fs = String(file.size());
if(file.isDirectory()){
data.println( "01-01-2000 00:00AM <DIR> " + fn);
} else {
data.println( "01-01-2000 00:00AM " + fs + " " + fn);
// data.println( " " + fn );
}
nm ++;
file = dir.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;
// Dir dir= SD.openDir(cwdName);
File dir= SD.open(cwdName);
//char dtStr[ 15 ];
// if(!SD.exists(cwdName))
if((!dir)||(!dir.isDirectory()))
client.println( "550 Can't open directory " +String(cwdName) );
// client.println( "550 Can't open directory " +String(parameters) );
else
{
// while( dir.next())
File file = dir.openNextFile();
// while( dir.openNextFile())
while( file)
{
String fn,fs;
fn = file.name();
// Serial.println(fn);
fn.remove(0, strlen(cwdName));
if(fn[0] == '/') fn.remove(0, 1);
fs = String(file.size());
if(file.isDirectory()){
data.println( "Type=dir;Size=" + fs + ";"+"modify=20000101000000;" +" " + fn);
// data.println( "Type=dir;modify=20000101000000; " + fn);
} else {
//data.println( "Type=file;Size=" + fs + ";"+"modify=20000101160656;" +" " + fn);
data.println( "Type=file;Size=" + fs + ";"+"modify=20000101000000;" +" " + fn);
}
nm ++;
file = dir.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;
// Dir dir=SD.openDir(cwdName);
File dir= SD.open(cwdName);
if( !SD.exists( cwdName ))
client.println( "550 Can't open directory " + String(parameters));
else
{
File file = dir.openNextFile();
// while( dir.next())
while( file)
{
// data.println( dir.fileName());
data.println( file.name());
nm ++;
file = dir.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, "r");
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, "w");
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" ))
{
#ifdef FTP_DEBUG
Serial.print("MKD P=");
Serial.print(parameters);
Serial.print(" CWD=");
Serial.println(cwdName);
#endif
String dir;
if (!strcmp(cwdName,"/")) // avoid "\\newdir"
{
dir = String("/") + parameters;
}
else
{
dir = String(cwdName) +"/" + parameters;
}
#ifdef FTP_DEBUG
Serial.print("try to create ");
Serial.println(dir);
#endif
fs::FS &fs = SD;
if (fs.mkdir(dir.c_str()))
{
client.println( "257 \"" + String(parameters) + "\" - Directory successfully created");
}
else
{
client.println( "502 Can't create \"" + String(parameters));
}
}
//
// RMD - Remove a Directory
//
else if( ! strcmp( command, "RMD" ))
{
#ifdef FTP_DEBUG
Serial.print("RMD ");
Serial.print(parameters);
Serial.print(" CWD=");
Serial.println(cwdName);
#endif
String dir;
if (!strcmp(cwdName,"/")) // avoid "\\newdir"
{
dir = String("/") + parameters;
}
else
{
dir = String(cwdName) +"/" + parameters;
}
fs::FS &fs = SD;
if (fs.rmdir(dir.c_str()))
{
client.println( "250 RMD command successful");
}
else
{
client.println( "502 Can't delete \"" + String(parameters)); //not support on espyet
}
}
//
// RNFR - Rename From
//
else if( ! strcmp( command, "RNFR" ))
{
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" ))
{
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 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, "r");
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)
// while (!dataServer.available() && millis() - startTime < 10000)
{
//delay(100);
yield();
}
if (dataServer.hasClient()) {
// if (dataServer.available()) {
data.stop();
data = dataServer.available();
#ifdef FTP_DEBUG
Serial.println("ftpdataserver client....");
#endif
}
}
return data.connected();
}
boolean FtpServer::doRetrieve()
{
//int16_t nb = file.readBytes((uint8_t*) buf, FTP_BUF_SIZE );
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;
}
unsigned long count=0;
boolean FtpServer::doStore()
{
if( data.connected() )
{
unsigned long ms0 = millis();
//Serial.print("Transfer=");
int16_t nb = data.readBytes((uint8_t*) buf, FTP_BUF_SIZE );
//unsigned long ms1 = millis();
//Serial.print(ms1-ms0);
if( nb > 0 )
{
// Serial.println( millis() << " " << nb << endl;
//Serial.print("SD=");
size_t written = file.write((uint8_t*) buf, nb );
/*
unsigned long ms2 = millis();
Serial.print(ms2-ms1);
Serial.print("nb=");
Serial.print(nb);
Serial.print("w=");
Serial.println(written);
*/
bytesTransfered += nb;
}
else
{
Serial.println(".");
}
return true;
}
closeTransfer();
return false;
}
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;
}
/*
* 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 <http://www.gnu.org/licenses/>.
*/
// 2017: modified by @robo8080
// 2019: modified by @HenrikSte
/*******************************************************************************
** **
** 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
//#include "Streaming.h"
#include "SD.h"
#include <FS.h>
#include <WiFiClient.h>
#define FTP_SERVER_VERSION "FTP-2016-01-14"
#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 (8192*1)-1 //512 // size of file buffer for read/write
class FtpServer
{
public:
FtpServer();
void begin(String uname, String pword);
int 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
/*
Carlos Almeida - 2200913
IPLEIRIA - Instituto Politécnico de Leiria
ESTG - Escola Superior de Tecnologia e Gestão
This project's code can be found in :
https://youtu.be/nAdJu2HUppY
*/
#include "Arduino.h"
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_task_wdt.h"
#include "Arduino.h"
#include <math.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <driver/i2s.h>
#include <exception>
#include <OLED_SSD1306_Chart.h>
#include "driver/i2c.h"
#include "esp_freertos_hooks.h"
#define I2S_WS 32
#define I2S_SD 33
#define I2S_SCK 26
#define ADC_1_0 4
#define ADC_RESOLUTION 12
#define ADC_RESOLUTION_2 4096.0
#define VREF_PLUS 3.3
#define VREF_MINUS 0.0
#define LED_PIN 5
#define I2S_PORT I2S_NUM_0
#define bufferLen 512
#define PIN_LM35 34 // ESP32 pin GIOP36 (ADC0) connected to LM35
#define ADC_VREF_mV 3300.0 // in millivolt
int freq = 5000;
int ledChannel = 5;
int resolution = 8;
//------------------------------------------
//-----------------DISPLAY------------------
//------------------------------------------
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
OLED_SSD1306_Chart display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define LOGO_HEIGHT 64
#define LOGO_WIDTH 128
volatile unsigned long ulIdleCycleCount = 0UL;
unsigned long lastInterruptTime = 0;
//------------------------------------------
//--------------------WIFI------------------
//------------------------------------------
#include <WiFi.h>
#include <NTPClient.h>
const char *ssid = "BuenosDias";
const char *password = "NaoSeiAPass";
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);
//------------------------------------------
//---------------------SD CARD-------------
//------------------------------------------
#include "FS.h"
#include "SD.h"
#include "SPI.h"
void listDir(fs::FS &fs, const char *dirname, uint8_t levels) {
Serial.printf("Listing directory: %s\n", dirname);
File root = fs.open(dirname);
if (!root) {
Serial.println("Failed to open directory");
return;
}
if (!root.isDirectory()) {
Serial.println("Not a directory");
return;
}
File file = root.openNextFile();
while (file) {
if (file.isDirectory()) {
Serial.print(" DIR : ");
Serial.println(file.name());
if (levels) {
listDir(fs, file.name(), levels - 1);
}
} else {
Serial.print(" FILE: ");
Serial.print(file.name());
Serial.print(" SIZE: ");
Serial.println(file.size());
}
file = root.openNextFile();
}
}
void createDir(fs::FS &fs, const char *path) {
Serial.printf("Creating Dir: %s\n", path);
if (fs.mkdir(path)) {
Serial.println("Dir created");
} else {
Serial.println("mkdir failed");
}
}
void removeDir(fs::FS &fs, const char *path) {
Serial.printf("Removing Dir: %s\n", path);
if (fs.rmdir(path)) {
Serial.println("Dir removed");
} else {
Serial.println("rmdir failed");
}
}
void readFile(fs::FS &fs, const char *path) {
Serial.printf("Reading file: %s\n", path);
File file = fs.open(path);
if (!file) {
Serial.println("Failed to open file for reading");
return;
}
Serial.print("Read from file: ");
while (file.available()) {
Serial.write(file.read());
}
file.close();
}
void writeFile(fs::FS &fs, const char *path, const char *message) {
Serial.printf("Writing file: %s\n", path);
File file = fs.open(path, FILE_WRITE);
if (!file) {
Serial.println("Failed to open file for writing");
return;
}
if (file.print(message)) {
Serial.println("File written");
} else {
Serial.println("Write failed");
}
file.close();
}
void writeFileAudio(fs::FS &fs, const char *path, const int16_t *buffer,
size_t length) {
/* Serial.printf("Writing file: %s\n", path);*/
File file = fs.open(path, FILE_APPEND);
if (!file) {
Serial.println("Failed to open file for writing");
return;
}
/*if (file.write(buffer,length)) {
// Serial.println("File written");
} else {
Serial.println("Write failed");
}*/
for (int i = 0; i < length; i++) {
file.write((int8_t) (buffer[i] & 0xff));
file.write((int8_t) (buffer[i] >> 8));
}
file.close();
}
void writeFileAudio_v2(const int16_t *buffer, size_t length) {
/* Serial.printf("Writing file: %s\n", path);*/
/*
File file = fs.open(path, FILE_APPEND);
if (!file) {
Serial.println("Failed to open file for writing");
return;
}
if (file.write(buffer,length*2)) {
// Serial.println("File written");
} else {
Serial.println("Write failed");
}
file.close();*/
FILE *fp;
fp = fopen("test_16_bits_data.bin", "ab");
fwrite(buffer, sizeof(int16_t), length, fp);
/*if (elements_written != 10) {
printf("Failed to write the entire buffer\n");
return 1;
}*/
fclose(fp);
printf("16 bits data written to file successfully\n");
}
void appendFile(fs::FS &fs, const char *path, const char *message) {
/* Serial.printf("Appending to file: %s\n", path);*/
/*String message_2="Appending to file:"+String(path)+"\n";
const char *pcMessageToPrint=message_2.c_str();
xQueueSendToBack( xPrintQueue, &( pcMessageToPrint), 0 );
*/
File file = fs.open(path, FILE_APPEND);
if (!file) {
/*String message_2="Failed to open file for appending\n";
const char *pcMessageToPrint=message_2.c_str();
xQueueSendToBack( xPrintQueue, &( pcMessageToPrint), 0 );*/
/* Serial.println("Failed to open file for appending");*/
return;
}
if (file.print(message)) {
/*String message_2="Message appended\n";
const char *pcMessageToPrint=message_2.c_str();
xQueueSendToBack( xPrintQueue, &( pcMessageToPrint), 0 );*/
/* Serial.println("Message appended");*/
} else {
/*String message_2="Append failed\n";
const char *pcMessageToPrint=message_2.c_str();
xQueueSendToBack( xPrintQueue, &( pcMessageToPrint), 0 );*/
/* Serial.println("Append failed");*/
}
file.close();
}
void renameFile(fs::FS &fs, const char *path1, const char *path2) {
Serial.printf("Renaming file %s to %s\n", path1, path2);
if (fs.rename(path1, path2)) {
Serial.println("File renamed");
} else {
Serial.println("Rename failed");
}
}
void deleteFile(fs::FS &fs, const char *path) {
Serial.printf("Deleting file: %s\n", path);
if (fs.remove(path)) {
Serial.println("File deleted");
} else {
Serial.println("Delete failed");
}
}
void testFileIO(fs::FS &fs, const char *path) {
File file = fs.open(path);
static uint8_t buf[512];
size_t len = 0;
uint32_t start = millis();
uint32_t end = start;
if (file) {
len = file.size();
size_t flen = len;
start = millis();
while (len) {
size_t toRead = len;
if (toRead > 512) {
toRead = 512;
}
file.read(buf, toRead);
len -= toRead;
}
end = millis() - start;
Serial.printf("%u bytes read for %u ms\n", flen, end);
file.close();
} else {
Serial.println("Failed to open file for reading");
}
file = fs.open(path, FILE_WRITE);
if (!file) {
Serial.println("Failed to open file for writing");
return;
}
size_t i;
start = millis();
for (i = 0; i < 2048; i++) {
file.write(buf, 512);
}
end = millis() - start;
Serial.printf("%u bytes written for %u ms\n", 2048 * 512, end);
file.close();
}
String testFileIO_String(fs::FS &fs, const char *path) {
File file = fs.open(path);
static uint8_t buf[512];
size_t len = 0;
uint32_t start = millis();
uint32_t end = start;
if (file) {
len = file.size();
size_t flen = len;
start = millis();
while (len) {
size_t toRead = len;
if (toRead > 512) {
toRead = 512;
}
file.read(buf, toRead);
len -= toRead;
}
end = millis() - start;
return String(flen);
/*Serial.printf("%u bytes read for %u ms\n", flen, end);*/
file.close();
} else {
/*Serial.println("Failed to open file for reading");*/
}
return "nothing";
/*
file = fs.open(path, FILE_WRITE);
if (!file) {
Serial.println("Failed to open file for writing");
return "Failed to open file for writing";
}
size_t i;
start = millis();
for (i = 0; i < 2048; i++) {
file.write(buf, 512);
}
end = millis() - start;
Serial.printf("%u bytes written for %u ms\n", 2048 * 512, end);
file.close();*/
}
//------------------------------------------
//-------------FREE RTOS--------------------
//------------------------------------------
void vTaskADC(void *pvParameters);
void vTaskLED(void *pvParameters);
void vTaskDisplay(void *pvParameters);
void vTaskWriteDataSD(void *pvParameters);
void vTaskServerFTP(void *pvParameters);
void vTaskCpuUsage(void *pvParameters);
void vTaskTime(void *pvParameters);
void vTaskAudio(void *pvParameters);
//Gatekeeper for the serial
static void vGatekeeperTaskSerial( void *pvParameters );
/* The service routine for the interrupt. This is the interrupt that the task
will be synchronized with. */
static void IRAM_ATTR vButtonInterruptHandler(void);
const char *pcTextForADC = "ADC is running\r\n";
const char *pcTextForPWM = "PWM is running\t\n";
const char *pcTextTime = "Time is adquiring\t\n";
const char *pcTextShowData = "Data acquired is being shown\t\n";
const char *pcTextWriteData = "Data acquired is being written\t\n";
QueueHandle_t xQueueSETPOINT_TEMP;
QueueHandle_t xQueueSETPOINT_TIME;
QueueHandle_t xQueueSETPOINT_DAY;
QueueHandle_t xQueueSETPOINT_AUDIO_MEAN;
QueueHandle_t xQueueSETPOINT_CPU_USAGE;
QueueHandle_t xPrintQueue;
QueueHandle_t xQueueSize;
SemaphoreHandle_t xBinarySemaphore_ResetGraphAtStart;
SemaphoreHandle_t xCountingSemaphore;
const uint8_t interruptPin = 2;
/* Used to hold the handle of Task2. */
TaskHandle_t xTaskAudioHandle;
TaskHandle_t xTaskPWMHandle;
// Tempo da última interruption (Debounce Circuit)
TickType_t xLastIntTime;
//Mutex for sd card usage
SemaphoreHandle_t xMutex_SD_CARD;
//--------------------------------------
//---------------MICROFONE-------------
//--------------------------------------
#include <driver/i2s.h>
#include <exception>
#define I2S_WS 32
#define I2S_SD 33
#define I2S_SCK 26
#define I2S_PORT I2S_NUM_0
#define bufferLen 512*2
// The I2S microphone's sample rate, bits per sample, and number of channels
const int sample_rate = 16000;
const int bits_per_sample = 16;
const int num_channels = 1;
void i2s_install() {
const i2s_config_t i2s_config = { .mode = i2s_mode_t(
I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX), .sample_rate = 16000,
.bits_per_sample = i2s_bits_per_sample_t(16), .channel_format =
I2S_CHANNEL_FMT_ONLY_LEFT,
//.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_STAND_MSB),
.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_I2S),
.intr_alloc_flags = 0, // default interrupt priority
//.dma_buf_count = 16,
.dma_buf_count = 10, .dma_buf_len = bufferLen, .use_apll = false,
.tx_desc_auto_clear = false, .fixed_mclk = -1, };
i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL);
}
void i2s_setpin() {
const i2s_pin_config_t pin_config = { .bck_io_num = I2S_SCK, .ws_io_num =
I2S_WS, .data_out_num = -1, .data_in_num = I2S_SD };
i2s_set_pin(I2S_PORT, &pin_config);
}
//--------------------------------------
//---------------WEB SERVER-------------
//--------------------------------------
#include "ESP32FtpServer.h"
FtpServer ftpSrv; //set #define FTP_DEBUG in ESP8266FtpServer.h to see ftp verbose on serial
void setup(void) {
Serial.begin(115200);
Serial.println("\nEU vou iniciar!");
//------------------------------------------------
//--------------------DISPLAY---------------------
//------------------------------------------------
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;)
; // Don't proceed, loop forever
}
display.clearDisplay();
vTaskDelay(10);
display.setCursor(0, 0);
display.setTextSize(2);
display.setTextColor(WHITE);
// Display static text
display.println(" AUDIO");
display.println(" &");
display.println(" DATA");
display.println(" RECORDER");
vTaskDelay(10);
display.setCursor(0, 10);
vTaskDelay(10);
vTaskDelay(10);
display.display();
//------------------------------------------------
//-----------------------WI-FI-------------------
//------------------------------------------------
Serial.println("\nEU vou configurar a net");
WiFi.begin(ssid, password);
vTaskDelay(2000);
while (WiFi.status() != WL_CONNECTED) {
vTaskDelay(3000);
Serial.println("Connecting to WiFi...");
WiFi.begin(ssid, password);
}
timeClient.begin();
vTaskDelay(100);
timeClient.update();
Serial.println(timeClient.getFormattedTime());
//------------------------------------------------
//-----------------SD CARD----------------------
//------------------------------------------------
do {
Serial.println("Card Mount Failed");
vTaskDelay(500);
} while (!SD.begin(5));
uint8_t cardType = SD.cardType();
if (cardType == CARD_NONE) {
Serial.println("No SD card attached");
return;
}
Serial.print("SD Card Type: ");
if (cardType == CARD_MMC) {
Serial.println("MMC");
} else if (cardType == CARD_SD) {
Serial.println("SDSC");
} else if (cardType == CARD_SDHC) {
Serial.println("SDHC");
} else {
Serial.println("UNKNOWN");
}
uint64_t cardSize = SD.cardSize() / (1024 * 1024);
Serial.printf("SD Card Size: %lluMB\n", cardSize);
listDir(SD, "/", 0);
Serial.printf("Total space: %lluMB\n", SD.totalBytes() / (1024 * 1024));
Serial.printf("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024));
ftpSrv.begin("esp32","esp32"); //username, password for ftp. set ports in ESP8266FtpServer.h (default 21, 50009 for PASV)
//------------------------------------------------
//-----------------I2S MICROPHONE-----------------
//------------------------------------------------
Serial.println("Setup I2S ...");
vTaskDelay(1000);
i2s_install();
i2s_setpin();
i2s_start(I2S_PORT);
vTaskDelay(500);
//AUDIO DIR
createDir(SD, "/audio_recording");
writeFile(SD, "/audio_recording/hello.txt", "Hello ");
//------------------------------------------------
//-------------------FREE RTOS START-------------
//------------------------------------------------
//TASKS
//QUEUES
//INTERRUPTS
//------------------------------------------------
vTaskPrioritySet(NULL, configMAX_PRIORITIES - 1);
vSemaphoreCreateBinary(xBinarySemaphore_ResetGraphAtStart);
xCountingSemaphore = xSemaphoreCreateCounting(2, 0);
pinMode(digitalPinToInterrupt(interruptPin), INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(interruptPin),
&vButtonInterruptHandler, FALLING);
xTaskCreatePinnedToCore(vTaskADC, "ADC", 2*1024, (void*) pcTextForADC, 3,
NULL, 1);
xTaskCreatePinnedToCore(vTaskCpuUsage, "CPU", 2*1024, (void*) pcTextForADC, 7,
NULL, 1);
xTaskCreatePinnedToCore(vTaskServerFTP, "FTP", 15*1024, (void*) pcTextForADC, 1,
NULL, 1);
xTaskCreatePinnedToCore(vTaskLED, "LED", 1024, (void*) pcTextForPWM, 5,
&xTaskPWMHandle, 1);
xTaskCreatePinnedToCore(vTaskDisplay, "Display", 4 * 1024,
(void*) pcTextShowData, 6, NULL, 1);
xTaskCreatePinnedToCore(vTaskWriteDataSD, "WriteData", 4 * 1024,
(void*) pcTextShowData, 4, NULL, 1);
xTaskCreatePinnedToCore(vTaskTime, "Time", 2 * 1024, (void*) pcTextTime, 3,
NULL, 1);
xTaskCreatePinnedToCore(vTaskAudio, "Audio", 15 * 1024, (void*) pcTextTime,
2, &xTaskAudioHandle, 1);
//Gatekeeper task
xTaskCreate( vGatekeeperTaskSerial, "Gatekeeper", 1024, NULL, 0, NULL );
xPrintQueue = xQueueCreate( 10, sizeof( char * ) );
xQueueSize = xQueueCreate(1, sizeof(String));
xQueueSETPOINT_TEMP = xQueueCreate(1, sizeof(float));
xQueueSETPOINT_TIME = xQueueCreate(1, sizeof(String));
xQueueSETPOINT_DAY = xQueueCreate(1, sizeof(String));
xQueueSETPOINT_AUDIO_MEAN = xQueueCreate(1, sizeof(float));
xQueueSETPOINT_CPU_USAGE = xQueueCreate(1, sizeof(int));
xLastIntTime = xTaskGetTickCount();
xMutex_SD_CARD = xSemaphoreCreateMutex();
esp_register_freertos_idle_hook(my_vApplicationIdleHook);
//------------------------------------------------
//--------------------PWM START-------------------
//------------------------------------------------
ledcSetup(ledChannel, freq, resolution);
ledcAttachPin(LED_PIN, ledChannel);
//------------------------------------------------
//-------------------SEMAPHORE INITIAL TAKE-------
//------------------------------------------------
xSemaphoreTake(xCountingSemaphore, portMAX_DELAY);
}
/*Lê o valor analógico do sensor de temperatura LM35, coloca-o na queue
xQueueSETPOINT_TEMP e coloca também a mensagem a mostrar na porta Serial na queue
xPrintQueue */
void vTaskADC(void *pvParameters) {
char *pcTaskName;
TickType_t xLastWakeTime;
int analog_value = 0;
float milliVolt = 0;
float tempC = 0;
pcTaskName = (char*) pvParameters;
analogReadResolution(ADC_RESOLUTION);
xLastWakeTime = xTaskGetTickCount();
for (;;) {
analog_value = analogRead(PIN_LM35);
// convert the ADC value to voltage in millivolt
milliVolt = analog_value * (ADC_VREF_mV / ADC_RESOLUTION_2);
// convert the voltage to the temperature in °C
tempC = milliVolt/10;
//tempC = milliVolt /10;
xQueueOverwrite(xQueueSETPOINT_TEMP, &tempC);
String message="ADC value: "+String(analog_value)+" Temperatura: "+String(tempC)+"\n";
const char *pcMessageToPrint=message.c_str();
xQueueSendToBack( xPrintQueue, &( pcMessageToPrint), 0 );
vTaskDelayUntil(&xLastWakeTime, (500 / portTICK_PERIOD_MS));
}
}
/*Verifica a hora e dia atual consultando um servidor NTP , coloca a hora e o dia nas queues
* xQueueSETPOINT_TIME e xQueueSETPOINT_DAY respectivamente e coloca também a mensagem a transmitir
* na porta Serial na queue xPrintQueue*/
void vTaskTime(void *pvParameters) {
char *pcTaskName;
TickType_t xLastWakeTime;
String time;
String day;
pcTaskName = (char*) pvParameters;
analogReadResolution(ADC_RESOLUTION);
xLastWakeTime = xTaskGetTickCount();
for (;;) {
/* Serial.print(pcTaskName);*/
timeClient.update();
time = timeClient.getFormattedTime();
day = String(timeClient.getDay());
String message="Day: "+day+" Hours :"+time+"\n";
const char *pcMessageToPrint=message.c_str();
xQueueSendToBack( xPrintQueue, &( pcMessageToPrint), 0 );
xQueueOverwrite(xQueueSETPOINT_TIME, &time);
xQueueOverwrite(xQueueSETPOINT_DAY, &day);
vTaskDelayUntil(&xLastWakeTime, (5000 / portTICK_PERIOD_MS));
}
}
/*Esta tarefa controla o LED, normalmente liga-o normalmente e quando o audio está a gravar no cartão SD
controla a intensidade do LED com um sinal PWM baseado no valor de audio atual, para tal vai consultar o
semáforo contador xCountingSemaphore , a queue xQueueSETPOINT_AUDIO_MEAN e transmite a sua mensagem
a mostrar na porta Serial para a queue xPrintQueue*/
void vTaskLED(void *pvParameters) {
char *pcTaskName;
TickType_t xLastWakeTime;
int freq = 5000;
int ledChannel = 5;
int resolution = 10;
int dutyCycle = 0;
pcTaskName = (char*) pvParameters;
ledcSetup(ledChannel, freq, resolution);
ledcAttachPin(LED_PIN, ledChannel);
xLastWakeTime = xTaskGetTickCount();
float mean;
for (;;) {
xQueuePeek(xQueueSETPOINT_AUDIO_MEAN, &mean, (500 / portTICK_PERIOD_MS));
String message="Audio mean from PWM is"+String(mean)+"\n";
const char *pcMessageToPrint=message.c_str();
xQueueSendToBack( xPrintQueue, &( pcMessageToPrint), 0 );
if(mean<0){
mean=25;
}
if(mean>1024)
{
mean=1024;
}
if (uxSemaphoreGetCount(xCountingSemaphore) == 1) {
for (int dutyCycle = 0; dutyCycle <= mean;
dutyCycle++) {
ledcWrite(ledChannel, dutyCycle);
vTaskDelay(1024 / mean);
}
for (int dutyCycle = mean; dutyCycle >= 0;
dutyCycle--) {
ledcWrite(ledChannel, dutyCycle);
vTaskDelay(1024 / mean);
}
} else {
ledcWrite(ledChannel, pow(2, resolution));
}
vTaskDelayUntil(&xLastWakeTime, (2000 / portTICK_PERIOD_MS));
}
}
/*Esta tarefa não faz uso de nenhum vtaskDelayUntil pois a leitura do áudio em si já
faz um delay até o buffer estiver cheio (neste caso 30ms). Está constantemente a adquirir
áudio, quando o semáforo contador chega ao seu primeiro incremento começa a gravar o
audio em pedaços de 30 ms no cartão SD.
O nome com que grava é dependente do dia/hora atual que vai consultar ás respetivas
queues e a gravação do audio na sua respetiva queue. O acesso ao cartão SD é controlado
através de um mutex xMutex_SD_CARD.
Quando esta tarefa está a gravar aumenta a sua própria prioridade de modo a evitar
interrupções não pretendidas, aumenta também a prioridade da tarefa vTaskLED para
que o LED continue a ter a sua intensidade variada com o audio.
Após ter sido terminada a gravação, é feito o give ao semáforo xBinarySemaphore_Rese-
tGraphAtStart para re-iniciar o gráfico no display*/
void vTaskAudio(void *pvParameters) {
static portBASE_TYPE run;
int16_t sBuffer[bufferLen];
String path;
unsigned portBASE_TYPE uxPriority;
unsigned portBASE_TYPE uxPriorityPWM;
String time;
String day;
String size ="0";
uxPriority = uxTaskPriorityGet( NULL);
uxPriorityPWM = uxTaskPriorityGet( xTaskPWMHandle);
for (;;) {
size_t bytesIn = 0;
esp_err_t result = i2s_read(I2S_PORT, &sBuffer, bufferLen, &bytesIn,
portMAX_DELAY);
if (result == ESP_OK) {
int samples_read = bytesIn / 8;
if (samples_read > 0) {
float mean = 0;
for (int i = 0; i < bytesIn; ++i) {
sBuffer[i] = sBuffer[i] * 2;
if(sBuffer[i]>0){
mean += (sBuffer[i]);
}
}
mean /= samples_read;
xQueueOverwrite(xQueueSETPOINT_AUDIO_MEAN, &mean);
time.replace(":","_");
//SEMAPHORE FOR BUTTON AND PROXIMITY SENSOR
if (uxSemaphoreGetCount(xCountingSemaphore) == 1) {
path = "/audio_recording/test" + day+"_"+time
+ ".raw";
//RAISING THE PRIORITY OF THIS TASK
//when writing audio on sd card
vTaskPrioritySet(xTaskAudioHandle, (uxPriority + 10));
vTaskPrioritySet(xTaskPWMHandle, (uxPriority + 11));
//MUTEX TO PROTECT THE USE OF SD CARD
xSemaphoreTake( xMutex_SD_CARD, portMAX_DELAY );
{
writeFileAudio(SD, path.c_str(), sBuffer, bufferLen);
}
xSemaphoreGive( xMutex_SD_CARD );
//Reseting the priority back to normal
vTaskPrioritySet(xTaskAudioHandle, (uxPriority));
vTaskPrioritySet(xTaskPWMHandle, (uxPriorityPWM));
}else{
xQueuePeek(xQueueSETPOINT_TIME, &time, 0);
xQueuePeek(xQueueSETPOINT_DAY, &day, 0);
}
if (uxSemaphoreGetCount(xCountingSemaphore) == 2) {
//Counting semaphore reset
xSemaphoreTake( xMutex_SD_CARD, portMAX_DELAY );
{
size = testFileIO_String(SD,path.c_str());
}
xSemaphoreGive( xMutex_SD_CARD );
xQueueOverwrite(xQueueSize, &size);
vTaskDelay(3000/ portTICK_PERIOD_MS);
vSemaphoreDelete(xCountingSemaphore);
xCountingSemaphore = xSemaphoreCreateCounting(2, 0);
xSemaphoreGive(xBinarySemaphore_ResetGraphAtStart);
}
}
}
/* ftpSrv.handleFTP();*/
}
}
/*Lé os dados a guardar no cartão SD, sendo eles a temperatura, hora , dia e utilização do processador das duas respectivas queues.
Guarda os dados lidos no cartão SD que tem o acesso controlado pelo mutex xMutex_SD_CARD e retorna uma mensagem para a queue xPrintQueue*/
void vTaskWriteDataSD(void *pvParameters) {
TickType_t xLastWakeTime;
float temperature;
String time;
String day;
int cpu_usage;
String temperature_mandar;
for (;;) {
xQueueReceive(xQueueSETPOINT_TEMP, &temperature, 0);
xQueueReceive(xQueueSETPOINT_TIME, &time, 0);
xQueueReceive(xQueueSETPOINT_DAY, &day, 0);
xQueueReceive(xQueueSETPOINT_CPU_USAGE,&cpu_usage,0);
temperature_mandar = String(temperature, 2);
//WRITING
//MUTEX TO PROTECT THE USE OF SD CARD
xSemaphoreTake( xMutex_SD_CARD, portMAX_DELAY );
{
appendFile(SD, "/data_from_esp32.txt", "DAY:");
appendFile(SD, "/data_from_esp32.txt", day.c_str());
appendFile(SD, "/data_from_esp32.txt", " HOURS:");
appendFile(SD, "/data_from_esp32.txt", time.c_str());
appendFile(SD, "/data_from_esp32.txt", " DATA:");
appendFile(SD, "/data_from_esp32.txt", temperature_mandar.c_str());
appendFile(SD, "/data_from_esp32.txt", " CPU USAGE:");
appendFile(SD, "/data_from_esp32.txt", (String(cpu_usage)).c_str());
appendFile(SD, "/data_from_esp32.txt", "\n");
}
xSemaphoreGive( xMutex_SD_CARD );
String message="Temperature and time written on sd card\n";
const char *pcMessageToPrint=message.c_str();
xQueueSendToBack( xPrintQueue, &( pcMessageToPrint), 0 );
vTaskDelayUntil(&xLastWakeTime, (5000 / portTICK_PERIOD_MS));
}
}
static void IRAM_ATTR vButtonInterruptHandler(void) {
static signed portBASE_TYPE xHigherPriorityTaskWoken;
xHigherPriorityTaskWoken = pdFALSE;
unsigned long currentTime = millis();
if (currentTime - lastInterruptTime < 200) {
return;
}
lastInterruptTime = currentTime;
/*Give to a counting semaphore */
xSemaphoreGiveFromISR(xCountingSemaphore,
(BaseType_t* )&xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken == pdTRUE) {
/* Giving the semaphore unblocked a task, and the priority of the
unblocked task is higher than the currently running task - force
a context switch to ensure that the interrupt returns directly to
the unblocked (higher priority) task.
NOTE: The syntax for forcing a context switch is different depending
on the port being used. Refer to the examples for the port you are
using for the correct method to use! */
portYIELD_FROM_ISR();
// vPortYield();
}
}
/*É a única tarefa que acede ao display, e tem a possibilidade de mostrar 3 ecrãs
diferentes.
O primeiro ecrã é o seu estado normal, onde mostra a temperatura e hora atual , tanto
como um gráfico do audio a ser detetado em tempo real. Para tal acede ás queues xQueue-
SETPOINT_TEMP, xQueueSETPOINT_TIME e xQueueSETPOINT_AUDIO_MEAN.
O segundo ecrã é mostrado assim que é começada a gravação do áudio, o que é
indicado pelo semáforo contador xCountingSemaphore , o segundo ecrã consiste apenas
na indicação que o audio está a ser gravado.
O terceiro ecrã é mostrado quando for terminada a gravação e mostra o tamanho total
do audio gravado, sendo que passados 3 segundos volta ao ecrã inicial*/
void vTaskDisplay(void *pvParameters) {
char *pcTaskName;
TickType_t xLastWakeTime;
float temperature;
String time;
pcTaskName = (char*) pvParameters;
String size_audio;
boolean display_once;
float mean;
xLastWakeTime = xTaskGetTickCount();
char *pcMessageToPrint;
static portBASE_TYPE run;
//-----------------------------------
//DRAWING A CHART FOR THE AUDIO GRAPH
//-----------------------------------
display.setChartCoordinates(0, 60); //Chart lower left coordinates (X, Y)
display.setChartWidthAndHeight(123, 40); //Chart width = 123 and height = 60
display.setXIncrement(5); //Distance between Y points will be 5px
display.setYLimits(0, 1000); //Ymin = 0 and Ymax = 100
display.setYLimitLabels("0", "1000"); //Setting Y axis labels
display.setYLabelsVisible(true);
display.setAxisDivisionsInc(12, 6); //Each 12 px a division will be painted in X axis and each 6px in Y axis
display.setPlotMode(SINGLE_PLOT_MODE); //Set single plot mode
display.setPointGeometry(POINT_GEOMETRY_NONE);
display.setLineThickness(LIGHT_LINE);
init_graph_com_valores(String(temperature),time);
for (;;) {
xQueuePeek(xQueueSETPOINT_TEMP, &temperature, 0);
xQueuePeek(xQueueSETPOINT_TIME, &time, 0);
xQueueReceive(xQueueSize, &size_audio, 0);
String message="Current semaphore counter is : "+String(uxSemaphoreGetCount(xCountingSemaphore))+"\n";
const char *pcMessageToPrint=message.c_str();
xQueueSendToBack( xPrintQueue, &( pcMessageToPrint), 0 );
if (uxSemaphoreGetCount(xCountingSemaphore) == 0) {
xQueuePeek(xQueueSETPOINT_AUDIO_MEAN, &mean, 0);
run= xSemaphoreTake( xBinarySemaphore_ResetGraphAtStart, (100 / portTICK_PERIOD_MS) );
if(run==pdPASS){
init_graph_com_valores(String(temperature),time);
}
if (!display.updateChart(mean)) //Value between Ymin and Ymax will be added to chart
{
init_graph_com_valores(String(temperature),time);
}
display_once = true;
}
if (uxSemaphoreGetCount(xCountingSemaphore) == 1) {
//Display only once because the audio acquisition is a continuous function
//So the processor will always be busy on that higher priority task
if (display_once == true) {
display.clearDisplay();
display.setCursor(0, 0);
display.setTextSize(2);
display.setTextColor(WHITE);
// Display static text
display.println(" AUDIO ");
vTaskDelay(50 / portTICK_PERIOD_MS);
display.println("RECORDING");
display.display();
display_once = false;
}
}
if (uxSemaphoreGetCount(xCountingSemaphore) == 2) {
display.clearDisplay(); //If chart is full, it is drawn again
display.setCursor(0, 0);
display.setTextSize(2);
display.setTextColor(WHITE);
vTaskDelay(100/portTICK_PERIOD_MS);
// Display static text
display.println("Audio has:");
vTaskDelay(50 / portTICK_PERIOD_MS);
display.println(size_audio);
display.println(" bytes");
display.display();
}
vTaskDelayUntil(&xLastWakeTime, (500 / portTICK_PERIOD_MS));
}
}
/*Esta tarefa controla a porta Serial, sendo a única tarefa que lhe acede. Todas as outras
tarefas quando querem mostrar algo na porta Serial, mandam a mensagem para a queue
xPrintQueue que é lida por esta tarefa, ao ler uma mensagem com sucesso transmite-a
para a porta Serial*/
static void vGatekeeperTaskSerial( void *pvParameters )
{
char *pcMessageToPrint;
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
for( ;; )
{
/* Wait for a message to arrive. */
xQueueReceive( xPrintQueue, &pcMessageToPrint, portMAX_DELAY );
/* There is no need to check the return value as the task will block
indefinitely and only run again when a message has arrived. When the
next line is executed there will be a message to be output. */
vTaskEnterCritical(&mux);
Serial.print(pcMessageToPrint );
Serial.flush();
vTaskExitCritical(&mux);
if (Serial.available()) {
vTaskEndScheduler();
}
/* Now simply go back to wait for the next message. */
}
}
/*Função auxiliar com o propósito de não re-escrever código, sendo que é usada várias vezes.
coloca os valores iniciais de temperatura, hora e desenha o gráfico*/
void init_graph_com_valores(String valor_recb_temp, String hora){
//-----------------------------------
//DISPLAYING DATA INITIALLY
//-----------------------------------
display.clearDisplay();
display.setCursor(0, 0);
display.setTextSize(1);
display.setTextColor(WHITE);
// Display static text
display.println("Temperature | Time");
display.print(valor_recb_temp);
display.print(" C |");
display.println(hora);
display.drawChart(); //Update the buffer to draw the cartesian chart
display.display();
}
/*Esta tarefa é o que controla os pedidos ao servidor FTP, tal como o pedido de ler o que está dentro do diretório ou
fazer download/upload de um ficheiro*/
void vTaskServerFTP(void *pvParameters){
TickType_t xLastWakeTime;
xLastWakeTime = xTaskGetTickCount();
for (;;){
ftpSrv.handleFTP(); //make sure in loop you call handleFTP()!!
vTaskDelayUntil(&xLastWakeTime, (100 / (portTICK_PERIOD_MS)));
}
}
/*Esta tarefa lê o contador de ciclos em idle que é incrementado sempre que o pro-
cessador fica em Idle, com esse contador calcula a utilização do processador. Transmite
o valor calculado para a queue xQueueSETPOINT_CPU_USAGE e transmite também
uma mensagem para a queue xPrintQueue para ser mostrada na porta Serial pelo gate-
keeper*/
void vTaskCpuUsage(void *pvParameters){
TickType_t xLastWakeTime;
xLastWakeTime = xTaskGetTickCount();
int cpu_usage;
int ciclos;
int ciclos_totais;
for (;;){
ciclos=ulIdleCycleCount;
//1.15 because it was noted that even if this task has a periodicity of 1 second, it takes around 1.15 seconds to repeat itself
ciclos_totais = (1.15) * configTICK_RATE_HZ;
cpu_usage=(100-((ciclos*100)/ciclos_totais));
String message="Ciclos: "+String(ciclos)+" Ciclos totais: "+String(ciclos_totais)+" Cpu usage "+String(cpu_usage)+"% \n";
const char *pcMessageToPrint=message.c_str();
xQueueSendToBack( xPrintQueue, &( pcMessageToPrint), 0 );
ulIdleCycleCount=0;
xQueueOverwrite(xQueueSETPOINT_CPU_USAGE, &cpu_usage);
vTaskDelayUntil(&xLastWakeTime, (1000 / (portTICK_PERIOD_MS)));
}
}
/* Idle hook functions MUST be called vApplicationIdleHook(), take no parameters,
and return void. */
bool my_vApplicationIdleHook( void )
{
//This method makes it so while on idle, the microprocessor increments a counter.
ulIdleCycleCount++;
return true;
}
void loop() {
vTaskDelete( NULL);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment