774 changes: 774 additions & 0 deletions src/GSMFTP.cpp

Large diffs are not rendered by default.

288 changes: 288 additions & 0 deletions src/GSMFTP.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,288 @@
/*
This file is part of the MKR GSM library.
Copyright (C) 2017 Arduino AG (http://www.arduino.cc/)
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
*/

#ifndef _GSM_FTP_H_INCLUDED
#define _GSM_FTP_H_INCLUDED

#include <Modem.h>

class GSMFTPElem;

class GSMFTP : public ModemUrcHandler {

public:
GSMFTP();
virtual ~GSMFTP();

/** Connect to a FTP server
@param hostname FTP server hostname
@param user FTP user name
@param password FTP password
@param port FTP server port
@param passiveMode true if passive mode is active
@return true if no error
*/
bool connect(String hostname, String user, String password, uint16_t port, bool passiveMode = true);
/** Disconnect to the FTP server
@return true if no error
*/
bool disconnect();
/** Get informations of remote directory
@param file class that contains the information of all the files found
@param show if true, display information of files
@param timeout maximum time allow to execute the function
@return true if no error
*/
bool ls(GSMFTPElem& file, bool show = false, uint32_t timeout = 10000);
/** Get informations of remote directory/file
@param file class that contains the information of all the files found
@param name name of file or directory where to search information
@param file name of file or directory where to search information
@param show if true, display information of files
@param timeout maximum time allow to execute the function
@return true if no error
*/
bool ls(GSMFTPElem& file, const String name, bool show = false, uint32_t timeout = 10000);
/** Create directory on the FTP server
@param name name of the directory to create
@param timeout maximum time allow to execute the function
@return true if no error
*/
bool mkdir(const String& name, uint32_t timeout = 10000);
/** Delete directory on the FTP server
@param name name of the directory to delete
@param timeout maximum time allow to execute the function
@return true if no error
*/
bool removeDirectory(const String&, uint32_t timeout = 10000);
/** Delete file on the FTP server
@param name name of the file to delete
@param timeout maximum time allow to execute the function
@return true if no error
*/
bool removeFile(const String&, uint32_t timeout = 10000);
/** Rename file on the FTP server
@param oldName name of the file to rename
@param name new name of the file to rename
@param timeout maximum time allow to execute the function
@return true if no error
*/
bool rename(const String& oldName, const String& name, uint32_t timeout = 10000);
/** Change of the working directory on the FTP server
@param path new working directory to move on
@param timeout maximum time allow to execute the function
@return true if no error
*/
bool cd(const String& path, uint32_t timeout = 10000);
/** Download a file from the FTP server
@param localFileName name of the file on filesystem sent from FTP server
@param remoteFileName name of file on FTP server to retreive on filesystem
@param timeout maximum time allow to execute the function, -1 infinite
@return true if no error
Download a file in blocking mode until the timeout elapsed.
*/
bool download(const String& localFileName, const String& remoteFileName, int32_t timeout = -1);
/** Start file download from the FTP server
@param localFileName name of the file on filesystem sent from FTP server
@param remoteFileName name of file on FTP server to retreive on filesystem
@return true if no error
Initialize the file download in non blocking mode.
*/
bool downloadStart(const String& localFileName, const String& remoteFileName);
/** Update download state
@param remoteFileName name of file on FTP server to retreive on filesystem
@param localFileName name of the file on filesystem sent from FTP server
@param showProgression if true show the downloading progression [%]
@return 1 : download finished, 0 downloading, -1 an error occured
Update the download in non blocking mode.
*/
int downloadReady(const String& localFileName, bool showProgression);
/** Upload a file to the FTP server
@param localFileName name of the file on filesystem to send to FTP server
@param timeout maximum time allow to execute the function, -1 infinite
@return true if no error
*/
bool upload(const String& localFileName, const String& remoteFileName, int32_t timeout = -1);
/** Start file upload to the FTP server
@param localFileName name of the file on filesystem to send to FTP server
@param remoteFileName name of the file on FTP server sent from filesystem
@return true if no error
Initialize the file upload in non blocking mode.
*/
bool uploadStart(const String& localFileName, const String& remoteFileName);
/** Update download state
@return 1 : upload finished, 0 downloading, -1 an error occured
Update the upload in non blocking mode.
When uploading no other request can be send to the server, so we can not display the progress
*/
int uploadReady();
/** Direct upload from local volatile memory to FTP server
@param data data to send
@param size data size to send
@param remoteFileName name of the file on FTP server
@param timeout maximum time before function return an error, -1 infinite
@return true if no error
*/
bool write(void* data, size_t size, const String& remoteFileName, int32_t timeout = -1);
/** Direct download from FTP server to local volatile memory
@param data data to received
@param size data size to received
@param remoteFileName name of the file on FTP server
@param timeout maximum time before function return an error, -1 infinite
@return true if no error
If size is less than the remote file size, the data reception is aborted before the complete downloaded.
In this case the connection can be lost.
*/
bool read(void* data, size_t size, const String& remoteFileName, int32_t timeout = -1);
/** Start upload in stream mode
@param remoteFileName name of the file on FTP server
@return true if no error
Set the module in direct link mode.
After that it will establish a transparent end to end communication
with the data connection TCP socket via the serial interface.
No command to the module can be executed until the end of the transfer.
*/
bool streamOutStart(const String& remoteFileName);
/** Send data to FTP server
@param data data to send
@param size data size to send
@return true if no error
Send a packet of data to the FTP server.
*/
bool streamOut(void* data, size_t size);
/** Finished the data transfer to FTP server
@return 1 : transfer finished, 0 busy, -1 an error occured
Exit direct link mode then wait for the transmission to be completed.
*/
int streamOutReady();
/** Start download in stream mode
@param remoteFileName name of the file on FTP server
@return true if no error
Set the module in direct link mode.
After that it will establish a transparent end to end communication
with the data connection TCP socket via the serial interface.
No command to the module can be executed until the end of the transfer.
*/
bool streamInStart(const String& remoteFileName);
/** Send data to FTP server
@param data data to receive
@param size data size to receive
@param timeout maximum time before function return an error, -1 infinite
@return 1 : all data is received, 0 not all data is received, -1 an error occured
Send a packet of data to the FTP server.
If all data is received the module will automatically exit from direct link mode.
*/
int streamIn(void* data, size_t size, int32_t timeout = -1);
/** Finished the data transfer to FTP server
@return 1 : transfer finished, 0 busy, -1 an error occured
Exit direct link mode then wait for the transmission to be completed.
*/
int streamInReady();
/** Print the error class and code of the last FTP operation
@brief
0,0 mean no error otherwise {error class},{error code}.
For the description refer to the documention :
https://www.u-blox.com/sites/default/files/u-blox-CEL_ATCommands_%28UBX-13002752%29.pdf
*/
void printError();

private:
static const uint32_t c_connectionTimeout = 10000;

void handleUrc(const String&);

int _connected;
int _dirCreated;
int _dirChanged;
int _fileRemoved;
int _dirRemoved;
int _fileRenamed;
int _fileDownloaded;
int _fileUploaded;
int _fileDirectUploaded;
int _fileDirectDownloaded;
GSMFTPElem* _fileInfo;
uint32_t _downloadDisplayTimeRef;
uint32_t _downloadRemoteFileSize;
uint32_t _uploadRemainingBytes;
};

class GSMFTPElem
{
public:
struct Elem
{
String permissions;
uint32_t size;
uint32_t number;
String user;
String group;
String lastModified;
String name;

Elem() :size(0), number(0)
{}
};

GSMFTPElem() :_elem(nullptr), _count(0) {}
~GSMFTPElem() { clear(); }

/** Append a new element in the file array
@param elem elem to append in the array
*/
void append(const Elem elem);
/** Show file information of the corresponding index
@param i index of the element to show
*/
void show(int i);
/** Clear the file array
*/
void clear();
/** Get a file element
@param i file index to get the element
@return array element relative to the index, empty element if the index is out of range
*/
Elem elem(uint16_t i);
/** Get number of file found
@return number of element in the array
*/
uint32_t count() { return _count; }
/** Parse string containing file information
@param str string containing file information to parse
*/
void parse(const String& str);

private:
Elem* _elem;
uint32_t _count;
};

#endif
188 changes: 188 additions & 0 deletions src/GSMFileSystem.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
/*
This file is part of the MKR GSM library.
Copyright (C) 2017 Arduino AG (http://www.arduino.cc/)
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
*/

#include <GSMFileSystem.h>
#include <Modem.h>

bool GSMFileSystem::ls(GSMFileSystemElem& file, bool show, uint32_t timeout)
{
uint32_t start = millis();
String response;
file.clear();
MODEM.send("AT+ULSTFILE=");

if (MODEM.waitForResponse(timeout, &response) == 1) {
file.parse(response);
}else {
return false;
}

for (int i = 0; i < file.count(); ++i){
if ((millis() - start) > timeout) {
return false;
}
file.setSize(i, size(file.elem(i).name));

if (show == true) {
file.show(i);
}
}
return true;
}

int32_t GSMFileSystem::size(const String& name)
{
String response;

MODEM.send("AT+ULSTFILE=2,\"" + name + "\"");

if (MODEM.waitForResponse(1000, &response) == 1) {
if (response.startsWith("+ULSTFILE: ")) {
return response.substring(11).toInt();
}
}
return -1;
}

bool GSMFileSystem::remove(const String& name)
{
MODEM.send("AT+UDELFILE=\"" + name + "\"");
if (MODEM.waitForResponse(10000) == 1) {
return true;
}else {
return false;
}
}

bool GSMFileSystem::remove(GSMFileSystemElem& file)
{
bool ok = true;
for (int i = 0; i < file.count(); ++i) {
ok &= remove(file.elem(i).name);
}
return ok;
}

uint32_t GSMFileSystem::freeSpace()
{
uint32_t res = 0;
String response;
MODEM.send("AT+ULSTFILE=1");

if (MODEM.waitForResponse(1000, &response) == 1) {
if (response.startsWith("+ULSTFILE: ")) {
res = response.substring(11).toInt();
}
}
return res;
}

bool GSMFileSystem::write(const String& fileName, void* data, size_t size)
{
MODEM.send("AT+UDWNFILE=\"" + fileName + "\"," + size);

if (MODEM.waitForPrompt() == 1) {
MODEM.write((const uint8_t*)data, size);
if (MODEM.waitForResponse(10000) == 1) {
return true;
}
}
return false;
}

bool GSMFileSystem::read(const String& fileName, void* data, size_t size)
{
String response;
MODEM.send("AT+URDFILE=\"" + fileName + "\"");

if (MODEM.waitForResponse(10000, &response) == 1) {
memcpy(data, response.c_str(), size);
return true;
}
return false;
}

//--- GSMFileSystemElem ---

GSMFileSystemElem::Elem GSMFileSystemElem::elem(uint16_t i)
{
if (i < _count) {
return _elem[i];
}
else {
return Elem();
}
}

void GSMFileSystemElem::setSize(uint16_t i, uint32_t size)
{
if (i < _count) {
_elem[i].size = size;
}
}

void GSMFileSystemElem::append(const Elem elem)
{
Elem* tmp = new Elem[_count + 1];
for (int i = 0; i < _count; ++i) {
tmp[i] = _elem[i];
}
tmp[_count] = elem;
if (_elem != nullptr) {
delete[] _elem;
}
_elem = tmp;
_count++;
}

void GSMFileSystemElem::clear() {
if (_elem != nullptr) {
delete[] _elem;
_elem = nullptr;
}
_count = 0;
}

void GSMFileSystemElem::show(int i)
{
if (i >= _count) {
return;
}
Serial.print(_elem[i].name);
Serial.print(" ");
Serial.println(_elem[i].size);
}

void GSMFileSystemElem::parse(const String& str)
{
String res = str;
int i = res.indexOf('"')+1;
int j = res.indexOf('"', i);

while ((i > 0) && (j > 0)){
Elem elem;
elem.name = res.substring(i, j);
res = res.substring(j + 1);
append(elem);
i = res.indexOf('"') + 1;
j = res.indexOf('"', i);
}
}

GSMFileSystem FILESYSTEM;
125 changes: 125 additions & 0 deletions src/GSMFileSystem.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
This file is part of the MKR GSM library.
Copyright (C) 2017 Arduino AG (http://www.arduino.cc/)
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
*/

#ifndef _GSM_FILE_SYSTEM_H_INCLUDED
#define _GSM_FILE_SYSTEM_H_INCLUDED

#include <Arduino.h>

class GSMFileSystemElem;

class GSMFileSystem {

public:

/** Get name and size of all the file system files
@param show if true, display name and size of the files
@param timeout maximum time allow to execute the function
@param file class that contains the information of all the files found
@return true if no error
*/
bool ls(GSMFileSystemElem& file, bool show = false, uint32_t timeout = 10000);
/** Delete a file of the file system
@param name name of the file to delete
@return true if no error
*/
bool remove(const String& name);
/** Delete files of the file system
@param files set of files to delete
@return true if no error
*/
bool remove(GSMFileSystemElem& files);
/** Get a free space of the file system in bytes
@return free space
*/
uint32_t freeSpace();
/** Create a file with data in the filesystem
@param name name of the file
@return size of the file, -1 if error
*/
int32_t size(const String& name);
/** Create a file with data in the filesystem
@param name name of the file to create
@param data address of the data to write
@param size size of the data to write
@return true if no error
*/
bool write(const String& name, void* data, size_t size);
/** Read a file in the filesystem
@param name name of the file to read
@param data address of read data
@param size size of the data to read
@return true if no error
*/
bool read(const String& name, void* data, size_t size);

};

class GSMFileSystemElem {

public:
struct Elem
{
String name;
uint32_t size;

Elem() :size(0)
{}
};

GSMFileSystemElem() :_elem(nullptr), _count(0){}
~GSMFileSystemElem() { clear(); }
/** Get file number of the file system
@return number of file read after the call of @ref ls function
*/
inline uint32_t count() { return _count; }
/** Get a file element
@param i file index to get the element
@return file element
*/
Elem elem(uint16_t i);
/** Get file size
@param i file index to get the size
@return file size
*/
void setSize(uint16_t i, uint32_t size);
/** Clear the file array
*/
void clear();
/** Append a new element in the file array
@param elem elem to append in the array
*/
void append(const Elem elem);
/** Show file information of the corresponding index
@param i index of the element to show
*/
void show(int i);
/** Parse string containing file information
@param str string containing file information to parse
*/
void parse(const String& str);

private:
Elem* _elem;
uint32_t _count;
};

extern GSMFileSystem FILESYSTEM;

#endif
54 changes: 52 additions & 2 deletions src/Modem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,33 @@ size_t ModemClass::write(const uint8_t* buf, size_t size)
return _uart->write(buf, size);
}

size_t ModemClass::read(uint8_t* buf, size_t size, int32_t timeout)
{
size_t res = 0;
uint32_t start = millis();

while ((((millis() - start) < timeout) || (timeout < 0))
&& (res < size)) {
if (_uart->available()) {
buf[res] = _uart->read();
res++;
}
}
return res;
}

void ModemClass::escapeSequence(uint32_t t1, uint32_t t2, bool waitResponse)
{
delay(t1);
_uart->write('+');
_uart->write('+');
_uart->write('+');
delay(t2);
if (waitResponse == true) {
_atCommandState = AT_RECEIVING_RESPONSE;
}
}

void ModemClass::send(const char* command)
{
if (_lowPowerMode) {
Expand Down Expand Up @@ -279,13 +306,36 @@ void ModemClass::poll()
}

case AT_RECEIVING_RESPONSE: {
if (c == '\n') {
if (_buffer.startsWith("+URDFILE: ")){
int i = _buffer.indexOf(',') + 1;
if (i < 0) {
return;
}
int j = _buffer.indexOf(',', i);
if (j < 0) {
return;
}
int size = _buffer.substring(i, j).toInt();
if (size == (_buffer.length() - j - 2)) {
_buffer = _buffer.substring(j + 2);
*_responseDataStorage = _buffer;
_responseDataStorage = nullptr;
_atCommandState = AT_COMMAND_IDLE;
_buffer = "";
_ready = 1;
return;
}
}
else if (c == '\n') {
_lastResponseOrUrcMillis = millis();

int responseResultIndex = _buffer.lastIndexOf("OK\r\n");
if (responseResultIndex != -1) {
_ready = 1;
} else {
}
else if (_buffer.lastIndexOf("CONNECT\r\n") != -1) {
_ready = 1;
}else {
responseResultIndex = _buffer.lastIndexOf("ERROR\r\n");
if (responseResultIndex != -1) {
_ready = 2;
Expand Down
2 changes: 2 additions & 0 deletions src/Modem.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ class ModemClass {

size_t write(uint8_t c);
size_t write(const uint8_t*, size_t);
size_t read(uint8_t*, size_t, int32_t timeout=-1);
void escapeSequence(uint32_t t1=1500, uint32_t t2=1500, bool waitResponse=false);

void send(const char* command);
void send(const String& command) { send(command.c_str()); }
Expand Down