Instead of naming the directories / files with their "word", I will do it using their language CODE, example:
CODE: es; word: ingles
CODE: en; word: english
CODE: fr; word: anglais
Inside the file "languages.txt", I have added its code at the beginning of each language / word, in this way it does not complicate anything for me to "use" the first 2 characters to generate the names, and the rest of the captured text, used to generate the audio.
es-esespañol,eninglés,frfrancés,
en-esspanish,enenglish,frfrench,
fr-esespagnol,enanglais,frfrançais,
Therefore, the first part of the line "en-" would indicate the language code to use, and in the following sections of the line, separated by "commas", the first 2 characters of each sub string, would be used to name directories and files, in this way, the use of special characters is avoided, unless they are used to be displayed by other means, such as screens that support them.
#include <SD.h>
#include <SPI.h>
#include <WiFi.h>
#include "Arduino.h"
#include <google-tts.h>
#include <HTTPClient.h>
HTTPClient http;
TTS tts;
const char* ssid = "MIWIFI_2G_dGTF";
const char* password = "MtFDtEDp";
//--------------------[ String formatSizeUnits ]--------------------------
// Function to represent only the size of the files in human readable
// format, can be issued in its entirety
String formatSizeUnits(unsigned long bytes){
String SizeUnit;
if (bytes >= 1073741824) { SizeUnit = String((bytes / 1073741824)) + " GB " + "[" + bytes + "]"; }
else if (bytes >= 1048576) { SizeUnit = String((bytes / 1048576)) + " MB " + "[" + bytes + "]"; }
else if (bytes >= 1024) { SizeUnit = String((bytes / 1024)) + " KB " + "[" + bytes + "]"; }
else if (bytes > 1) { SizeUnit = String(bytes) + " bytes " + "[" + bytes + "]"; }
else if (bytes == 1) { SizeUnit = String(bytes) + " byte " + "[" + bytes + "]"; }
else { SizeUnit = "0 bytes"; }
return SizeUnit;
}
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
// This portion of code is only for testing
// using the messages through the serial port.
#define debugprogram 1 // 1: Send messages to serial port
// 0: Disable serial messaging
#if debugprogram == 1
#define PRINT(x) Serial.print(x)
#define PRINTLN(x) Serial.println(x)
#else
#define PRINT(x)
#define PRINTLN(x)
#endif
//-----------------------------------------------------------------------
File root; // Needed to use the SD library
int tasks = 0; // Multipurpose variable to control the "ReadFile"
// function and other aspects of module programming
int FileLines = 0; // variable that stores the number of lines in a file
String chosenlanguage; // Variable that stores the selected language
//----------------------[ void MakeDirectory ]----------------------------
// Function to create directories
void MakeDirectory(String Directory) {
String dir = "/" + Directory;
if (SD.exists( dir )) {
PRINTLN( "Directory: \" " + dir + " \" exist\n" );
} else {
PRINTLN( "Directory: \" " + dir + " \" don´t exist, making it!" );
SD.mkdir( dir );
PRINTLN( dir );
}
}
//------------------------------------------------------------------------
void AudioDownload(String URL, String FileDirectory, String FileName){
String Mp3FileName = FileName + String( ".mp3" );
http.begin(URL); // We make the call to the address
int httpCode = http.GET(); // We capture the code returned by the URL address
if(httpCode == 200) { // Code 200 means that everything has gone well
String payload = http.getString(); // We take the data from the embedded data/audio file from the URL
// and temporarily save it in a string variable
PRINT( "Size of embebed HTTP file: " );
PRINTLN(formatSizeUnits(payload.length()));
File f = SD.open( FileDirectory + '/' + Mp3FileName, FILE_WRITE );
if ( !f ) { PRINTLN("Could not create file"); while (1); }
else { f.print(payload); PRINTLN("File was created"); } // We write the data in the audio file and close it.
f.close(); // Force the file to close after an successfully or fail writing.
} else { // If the call to the URL address failed, we print the error code
Serial.printf( "[HTTP] GET... failed, error: %s\n", http.errorToString( httpCode ).c_str() );
PRINTLN( "" );
}
http.end(); // Close/end the http connection
}
//-------------------------[ void ReadFile ]------------------------------
// General purpose function, which encompasses everything related
// to creating, reading and writing files and folders
//------------------------------------------------------------------------
void ReadFile( String FileName, int ReadFileOpcion ) {
// FileName: Function target file name
// ReadFileOption: option to use only one function and simplify programming
root = SD.open( "/" + FileName + ".txt", FILE_READ );
int LineNumRead = 1;
bool found = false; //
if ( root.available() > 0 ) {
PRINTLN ( "File: \" " + FileName + " \" exist, accessing ...\n" );
while ( root.available() > 0 ) {
switch ( ReadFileOpcion ) {
case 1: { // This function only serves to "count" the lines in the desired file
String FL = root.readStringUntil( '\n' ); // Read the selected file "line-by-line" using line feed as limit
LineNumRead += 1; // Every time it reaches the end of a line, we increase the value of "lines read" counter.
}
FileLines = LineNumRead - 1;
break;
case 2: {
String FL = root.readStringUntil( '\n' ); // Read the selected file "line-by-line" using line feed as limit
int init = FL.indexOf( '-' );
String lang1 = FL.substring( 0, init ); // Get the content of the entire line
PRINTLN( "Actual line: " + String( LineNumRead ) ); // Pointer to the actual read line IN the file
if ( lang1 == chosenlanguage ) {
found = true; // If the selected CODE is found, we proceed
PRINTLN( "- CODE: " + chosenlanguage + ", exist in line: " + LineNumRead + ", inside of file: " + FileName );
PRINTLN( "- The languages translated for this code are as follows:" );
MakeDirectory( FileName + '/' + lang1 ); // We create the directory that will contain the audios of the
// selected language
while ( found == true ) {
int last = FL.indexOf( ',', init + 1 ); // Capture the occurrence of the comma, to use it as a pointer
// into the String containing the line of text
if ( last < 0){ break; } // Force to etect if there are no more separators, and we exit the process
String lang2 = FL.substring( init+1, last ); // Detect the following subString within the current reading
// line and pass it to the serial port (or for any other use,
// for example: enter these text strings in a menu).
String lang3 = lang2.substring( 0,2 );
String lang4 = lang2.substring( 2 );
String GTL = tts.getSpeechUrl( lang4, lang1 ); // We generate the link for the http request of google translator
MakeDirectory( FileName + '/' + lang1 + '/' + lang3 ); // Directory that will contain the translated languages
AudioDownload( GTL, '/' + FileName + '/' + lang1, lang3 ); // Download / create the *.mp3 file
PRINTLN(GTL); //
PRINTLN( '/' + FileName + '/' + lang1 + '/' + lang3 );
PRINTLN( " * " + lang4 );
PRINTLN( " " );
init = last; // We assign the next "initial" position, the "end" of the previous one,
// in this way we can reuse the code without adding anything new
}
}
}
LineNumRead += 1;
}
if ( found == true ) { // If the language CODE is found, please stop reading / searching, so we make this process
// faster and we don't waste time reading the rest of the file
PRINTLN( "CODE language found!" );
break;
}
}
if ( found != true && ReadFileOpcion == 2 ){ // If we read the "languages" file and we do not find the CODE, we launch an alert
PRINTLN( "- The selected CODE language: " + chosenlanguage + " is not present in file: " + FileName );
}
} else { // If the "languages" file is not found, we launch an ALERT, this can be used later to "download" this file from the internet cloud
PRINTLN( "The file cannot be read or does not exist\n" );
}
LineNumRead = 1;
root.close(); // Force to close any open file, in this case "root"
}
void setup(){
Serial.begin(112500); // Initialize the serial port
WiFi.mode( WIFI_STA );
WiFi.begin( ssid, password );
while (WiFi.status() != WL_CONNECTED) {
delay(250);
Serial.print( "." );
}
SD.begin(5); // Initialize the SD module
chosenlanguage = "en"; // We select the default language
tasks = 1;
if(WiFi.status() == WL_CONNECTED) {
PRINTLN( "Connected to WIFI network, starting ONLINE MODE..." );
} else {
PRINTLN( "WIFI connection failed, starting OFFLINE MODE ..." );
}
}
// We use the variable "tasks", to tell the program where to start
void loop(){
switch (tasks) {
case 1:
MakeDirectory( "languages" ); // First we create the "root" folder that will contain all the audios necessary for the application
ReadFile( "languages", 1 ); // Count the lines present in the file "languages.txt"
ReadFile( "languages", 2 ); // Find the language CODE in the file, and load the translated languages for it
tasks += 1;
break;
}
}
The result is this, creation of the directories and download of the audios for their respective languages.
I repeat, if someone knows how to do something much better than me, has any advice or correction, please let me know, it would help me a lot.