The serial communication is a general communication method because of ease of use. Especially the RS232 port was much popular because most personal computers were embedded it for a long time. So a lot of devices equipt a serial port(usually RS232 port) in order to output debug or monitor data.
When you check your device you need to monitor its console port which interface is (usually) RS232. But it might be very hard to monitor it if your device is on your location or you cannot keep your loacation for a long time.
The idea of this project is to get you console data from your serial port and transmit these to the remote server by using FTP. (The FTP is a wide-spread protocol to transmit files.)
I have implemented a "FTP data logger" by using PHPoC which transmit the serial data from the console port to remote FTP server.
Components
- P4S-341 - PHPoC Black
- PES-2201 - RS232 Expension Board for PHPoC
Software Structure and Operation
The PHPoC Black gets serial data from the PES-2201(RS232 expansion board) and transmits these to remote FTP server when received data size is over pre-configured vaule or timeout has been expired. The directory will be created every date and data are stored to files which are created every hours on the FTP server.
The software is composed with web configuration, FTP client library, and main task based on configuration.
Sample System Configuration
I have tested my implementation with a Cisco AP. The Cisco AP is outputting console data every events.
- P4S-341 - PHPoC Black
- PES-2201 - RS232 Expension Board for PHPoC
- Cisco Aironet 1600 Series AP - The target device to monitor
- Filezilla FTP server on Windows 10 - FTP server
Operation
I implemented it to configure IP address, serial port, FTP server related parameters and packing methods with web interface.
The following screenshot is the Filezilla's log which displays uploading data.
The following two screenshot shows directories according to date and files according to hour.
The following screenshot is the file contents which uploaded to the FTP server from the Cisco AP's console port. You can see a lot of information from the AP.
The FTP client functions
I have implemented over 20 functions which are compatible to the php.net FTP related functions as followed.
Source
Please download full source codes from its attachment.
PHP Code:
<?php
if(_SERVER("REQUEST_METHOD"))
exit; // avoid php execution via http request
include_once "/lib/vn_tcp_mc.php";
include_once "/lib/sd_340.php";
define("REPLY_TIMEOUT", 1000);
define("FTP_ASCII", "A");
define("FTP_IMAGE", "I");
$cmd_id = 0;
$data_id = 1;
$pasv_ip = "";
$pasv_port = 0;
define ("FTP_TIMER_ID", 0);
$rbuf = "";
function vn_ftp_data_client()
{
global $data_id;
global $pasv_ip, $pasv_port;
tcp_client($data_id, $pasv_ip, $pasv_port);
st_free_setup(FTP_TIMER_ID, "sec");
do{
if(tcp_state($data_id) == 4)
{
echo "data connection OK.\r\n";
return TRUE;
}
}
while(st_free_get_count(FTP_TIMER_ID) < 5);
echo "data connection fail.\r\n";
return FALSE;
}
function vn_ftp_get_reply($timeout_ms = 1000)
{
global $cmd_id;
$rbuf = "";
$lbuf = "";
$ret = 0;
st_free_setup(FTP_TIMER_ID, "ms");
do{
$len = tcp_read_until($cmd_id, $lbuf, "\r\n");
$ret += $len;
$rbuf .= $lbuf;
if($len >= 4 && substr($lbuf, 3, 1) == " ")
{
echo "<< $rbuf";
return $rbuf;
}
}while($timeout_ms > st_free_get_count(FTP_TIMER_ID));
return "";
}
function vn_get_unix_time($year, $month, $mday, $hour, $min, $sec, $adj_h)
{
$dmonth = array(0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365);
$iday = 365 * ($year-1970) + $dmonth[$month-1] + ($mday - 1); // reg. days since 1/1/70
$iday += ($year - 1969) / 4; // add leap days since 1/1/70
if(($month > 2) && ($year % 4 == 0)) $iday++; // if leap year and past Feb add a day
$ret = $sec + 60 * ($min + 60 * ($hour + 24 * $iday)); // compute seconds since 1970
//$ret += 32400; // set to UTC (Korea zone: +9)
$ret += ($adj_h * 3600); // set to UTC (Korea zone: +9)
return $ret;
}
function ftp_pasv($pasv)
{
global $cmd_id;
global $pasv_ip, $pasv_port;
$pasv_param = array();
echo ">> PASV\r\n";
tcp_write($cmd_id, "PASV\r\n");
$ret = vn_ftp_get_reply(REPLY_TIMEOUT);
if(substr($ret, 0, 3) === "227")
{
echo "PASSIVE Mode.\r\n";
$ret = substr($ret, strpos($ret, "(") + 1);
$pasv_param = explode(",", $ret);
$pasv_ip = $pasv_param[0] . ".";
$pasv_ip .= $pasv_param[1] . ".";
$pasv_ip .= $pasv_param[2] . ".";
$pasv_ip .= $pasv_param[3];
echo "pasv_ip : $pasv_ip\r\n";
$pasv_port = ((int)$pasv_param[4] * 256) + (int)$pasv_param[5];
echo "pasv_port = $pasv_port\r\n";
return TRUE;
}
return FALSE;
}
function vn_ftp_append_put($remote_file, $local_file, $mode, $cmd)
{
global $cmd_id, $data_id;
if(ftp_pasv("I") == FALSE) return FALSE;
if(vn_ftp_data_client() == FALSE) return FALSE;
echo ">> TYPE $mode\r\n";
tcp_write($cmd_id, "TYPE $mode\r\n");
$rets = vn_ftp_get_reply(REPLY_TIMEOUT);
if(substr($rets, 0, 3) != "200")
{
echo "failed to set to mode $mode.\r\n";
return FALSE;
}
echo "set to mode $mode.\r\n";
echo ">> $cmd $remote_file\r\n";
tcp_write($cmd_id, "$cmd $remote_file\r\n");
$rets = vn_ftp_get_reply(REPLY_TIMEOUT);
if(substr($rets, 0, 3) === "150")
{
echo "ret = $rets\r\n";
$len = tcp_write($data_id, $local_file);
}
else echo "ret = $rets\r\n";
sleep(1);
tcp_disconnect($data_id);
$rets = vn_ftp_get_reply(REPLY_TIMEOUT);
if(substr($rets, 0, 3) === "226")
if($len == strlen($local_file))
{
echo "data put OK.\r\n";
return TRUE;
}
else
{
echo "data put NG.\r\n";
return FALSE;
}
}
function vn_ftp_close()
{
global $cmd_id;
echo ">> QUIT\r\n";
tcp_write($cmd_id, "QUIT\r\n");
$ret = vn_ftp_get_reply(REPLY_TIMEOUT);
if(substr($ret, 0, 3) == "221")
{
echo "logged out.\r\n";
}
else
{
echo "fail to logout.\r\n";
}
tcp_disconnect($cmd_id);
return TRUE;
}
function ftp_append($remote_file, $local_file, $mode = FTP_IMAGE)
{
return vn_ftp_append_put($remote_file, $local_file, $mode, "APPE");
}
function ftp_cdup()
{
global $cmd_id;
tcp_write($cmd_id, "CDUP\r\n");
$rets = vn_ftp_get_reply(REPLY_TIMEOUT);
if(substr($rets, 0, 3) === "200")
{
$str_array = array();
$str_array = explode(""", $rets, 3);
$cwd = $str_array[1];
echo "changed to parent directory: $cwd.\r\n";
return TRUE;
}
else
{
echo "failed to change to parent directory.\r\n";
return FALSE;
}
}
function ftp_chdir($directory)
{
global $cmd_id;
tcp_write($cmd_id, "CWD $directory\r\n");
$rets = vn_ftp_get_reply(REPLY_TIMEOUT);
if(substr($rets, 0, 3) === "250")
{
/* some FTP servers don't return directory name
$str_array = array();
$str_array = explode(""", $rets, 3);
$cwd = $str_array[1];
echo "directory changed: $cwd.\r\n";
*/
echo "directory changed: $directory.\r\n";
return TRUE;
}
else
{
echo "failed to change directory to $directory.\r\n";
return FALSE;
}
}
function ftp_close()
{
return vn_ftp_close();
}
function ftp_connect($host, $port = 21, $timeout = 90)
{
global $cmd_id;
tcp_client($cmd_id, $host, $port);
st_free_setup(FTP_TIMER_ID, "sec");
do{
$state = tcp_state($cmd_id);
if($state == 4)
{
echo "Connected to the FTP server($host:$port)\r\n";
$ret = vn_ftp_get_reply(REPLY_TIMEOUT);
if(substr($ret, 0, 3) == "220")
{
echo "The FTP service is available.\r\n";
return TRUE;
}
else return FALSE;
}
}while($timeout > st_free_get_count(FTP_TIMER_ID));
echo "connection timeout to the ftp server($host:$port)\r\n";
return FALSE;
}
function ftp_delete($path)
{
global $cmd_id;
tcp_write($cmd_id, "DELE $path\r\n");
$rets = vn_ftp_get_reply(REPLY_TIMEOUT);
if(substr($rets, 0, 3) === "250")
{
echo "$path deleted.\r\n";
return TRUE;
}
else
{
echo "failed to delete $path.\r\n";
return FALSE;
}
}
function ftp_get($local_file, $remote_file, $mode = FTP_IMAGE, $resumepos = 0)
{
global $cmd_id, $data_id;
global $rbuf;
if(ftp_pasv("I") == FALSE) return FALSE;
if(vn_ftp_data_client() == FALSE) return FALSE;
echo ">> TYPE $mode\r\n";
tcp_write($cmd_id, "TYPE $mode\r\n");
$rets = vn_ftp_get_reply(REPLY_TIMEOUT);
if(substr($rets, 0, 3) != "200")
{
echo "failed to set to mode $mode.\r\n";
return FALSE;
}
echo "set to mode $mode.\r\n";
if($resumepos != 0)
{
$len = tcp_write($cmd_id, "REST $resumepos\r\n");
$rets = vn_ftp_get_reply(REPLY_TIMEOUT);
if(substr($rets, 0, 3) !== "350")
{
echo "ret = $rets\r\n";
return FALSE;
}
echo "ret = $rets\r\n";
}
$len = tcp_write($cmd_id, "RETR $remote_file\r\n");
$rbuf = "";
$buf = "";
while(1)
{
$len =tcp_read($data_id, $buf);
if($len < 0) break;
$rbuf .= $buf;
}
echo "retried data: \r\n$rbuf\r\n";
$rets = vn_ftp_get_reply(REPLY_TIMEOUT);
if(substr($rets, 0, 3) === "226")
{
if($len == strlen($local_file))
{
echo "data put OK.\r\n";
return TRUE;
}
else
{
echo "data put NG.\r\n";
return FALSE;
}
}
return TRUE;
}
function ftp_login($username, $password)
{
global $cmd_id;
echo ">> USER $username\r\n";
tcp_write($cmd_id, "USER $username\r\n");
$ret = vn_ftp_get_reply(REPLY_TIMEOUT);
if(substr($ret, 0, 3) == "230")
{
echo "FTP logged in.\r\n";
return TRUE;
}
else if(substr($ret, 0, 3) == "331")
{
echo ">> PASS $password\r\n";
tcp_write($cmd_id, "PASS $password\r\n");
$ret = vn_ftp_get_reply(REPLY_TIMEOUT);
if(substr($ret, 0, 3) == "230")
{
echo "FTP logged in.\r\n";
return TRUE;
}
}
echo "FTP login failed.\r\n";
return FALSE;
}
//RFC3659
function ftp_mdtm($remote_file)
{
global $cmd_id;
tcp_write($cmd_id, "MDTM $remote_file\r\n");
$rets = vn_ftp_get_reply(REPLY_TIMEOUT);
if(substr($rets, 0, 3) === "213")
{
$str_array = array();
$str_array = explode(" ", $rets, 3);
$lmd = $str_array[1];
$unix_time = vn_get_unix_time((int)substr($lmd, 0, 4), (int)substr($lmd, 4, 2),
(int)substr($lmd, 6, 2), (int)substr($lmd, 8, 2),
(int)substr($lmd, 10, 2), (int)substr($lmd, 12, 2), 9);
//$ret = date("Y:M-d-TH:i:s", $lmd);
echo "last modified date of $remote_file : $lmd.\r\n";
return $unix_time;
}
else
{
echo "failed to getting last modified date of $remote_file.\r\n";
return -1;
}
}
function ftp_mkdir($directory)
{
global $cmd_id;
tcp_write($cmd_id, "MKD $directory\r\n");
$rets = vn_ftp_get_reply(REPLY_TIMEOUT);
if(substr($rets, 0, 3) === "257")
{
$str_array = array();
$str_array = explode(""", $rets, 3);
$directory = $str_array[1];
echo "new directory: $directory.\r\n";
return $directory;
}
else
{
echo "failed to make a directory to $directory.\r\n";
return FALSE;
}
}
function ftp_nlist($directory)
{
global $cmd_id, $data_id;
global $rbuf;
if(ftp_pasv("I") == FALSE) return FALSE;
if(vn_ftp_data_client() == FALSE) return FALSE;
echo ">> TYPE A\r\n";
tcp_write($cmd_id, "TYPE A\r\n");
$rets = vn_ftp_get_reply(REPLY_TIMEOUT);
if(substr($rets, 0, 3) != "200")
{
echo "failed to set to mode A.\r\n";
return FALSE;
}
echo "set to mode A.\r\n";
$len = tcp_write($cmd_id, "NLST $directory\r\n");
$rbuf = "";
$buf = "";
while(1)
{
$len = tcp_read($data_id, $buf);
if($len < 0) break;
$rbuf .= $buf;
}
echo "directory list: \r\n$rbuf\r\n";
$rets = vn_ftp_get_reply(REPLY_TIMEOUT);
if(substr($rets, 0, 3) === "125" || substr($rets, 0, 3) === "150")
{
echo substr($rets, 0, 3) . "\r\n";
}
$rets = vn_ftp_get_reply(REPLY_TIMEOUT);
if(substr($rets, 0, 3) === "226" || substr($rets, 0, 3) === "250")
{
$reta = array();
$reta = explode("\r\n", $rbuf);
return $reta;
}
else
{
echo "nlist command failed.\r\n";
return FALSE;
}
}
function ftp_put($remote_file, $local_file, $mode = FTP_IMAGE, $start_pos = 0)
{
global $cmd_id, $data_id;
// $start_pos param is not supported now
if($start_pos != 0)
{
echo "only support \$start_pos = 0\r\n";
return FALSE;
}
return vn_ftp_append_put($remote_file, $local_file, $mode, "STOR");
}
function ftp_pwd()
{
global $cmd_id;
tcp_write($cmd_id, "PWD\r\n");
$rets = vn_ftp_get_reply(REPLY_TIMEOUT);
if(substr($rets, 0, 3) === "257")
{
$str_array = array();
$str_array = explode(""", $rets, 3);
$cwd = $str_array[1];
echo "current working directory: $cwd.\r\n";
return $cwd;
}
else
{
echo "ftp command failed: PWD.\r\n";
return FALSE;
}
}
function ftp_quit()
{
return vn_ftp_close();
}
function ftp_raw($command)
{
global $cmd_id;
tcp_write($cmd_id, "$command\r\n");
return vn_ftp_get_reply();
}
function ftp_rename($oldname, $newname)
{
global $cmd_id;
tcp_write($cmd_id, "RNFR $oldname\r\n");
$rets = vn_ftp_get_reply(REPLY_TIMEOUT);
if(substr($rets, 0, 3) != "350")
{
echo "RNFR command failed.\r\n";
return FALSE;
}
tcp_write($cmd_id, "RNTO $newname\r\n");
$rets = vn_ftp_get_reply(REPLY_TIMEOUT);
if(substr($rets, 0, 3) == "250")
{
echo "rename OK.\r\n";
return TRUE;
}
else
{
echo "RNTO command failed.\r\n";
return FALSE;
}
}
function ftp_rmdir($directory)
{
global $cmd_id;
tcp_write($cmd_id, "RMD $directory\r\n");
$rets = vn_ftp_get_reply(REPLY_TIMEOUT);
if(substr($rets, 0, 3) === "250")
{
echo "directory removed: $directory.\r\n";
return TRUE;
}
else
{
echo "failed to remove directory: $directory.\r\n";
return FALSE;
}
}
function ftp_site($command)
{
global $cmd_id;
tcp_write($cmd_id, "SITE $command\r\n");
$rets = vn_ftp_get_reply(REPLY_TIMEOUT);
if(substr($rets, 0, 3) !== "200")
{
echo "failed SITE $command command.\r\n";
return FALSE;
}
return TRUE;
}
//RFC3659
function ftp_size($remote_file)
{
global $cmd_id;
tcp_write($cmd_id, "SIZE $remote_file\r\n");
$rets = vn_ftp_get_reply(REPLY_TIMEOUT);
if(substr($rets, 0, 3) === "213")
{
$str_array = array();
$str_array = explode(" ", $rets, 3);
$size = (int)$str_array[1];
echo "$remote_file size: $size.\r\n";
return $size;
}
else
{
echo "failed to getting $remote_file size.\r\n";
return -1;
}
}
function ftp_systype()
{
global $cmd_id;
tcp_write($cmd_id, "SYST\r\n");
$rets = vn_ftp_get_reply(REPLY_TIMEOUT);
if(substr($rets, 0, 3) === "215")
{
$systype = substr($rets, 4);
echo "system type: $systype.\r\n";
return $systype;
}
else
{
echo "failed to getting system type.\r\n";
return FALSE;
}
}
// custom function
function ftp_cmd_session_state()
{
global $cmd_id;
return tcp_state($cmd_id);
}
// custom function
function ftp_data_session_state()
{
global $data_id;
return tcp_state($data_id);
}
?>
PHP Code:
<?php
if(_SERVER("REQUEST_METHOD"))
exit; // avoid php execution via http request
include_once "lib/sd_340.php";
include_once "lib/vn_ftp.php";
include_once "envs.php";
include_once "parse.php";
define ("THRES_TIMER_ID", 1);
$pid_envu = pid_open("/mmap/envu");
$buf = "";
pid_read($pid_envu, $buf);
$ftp_server = find_value_with_key($buf, "ftp_server" , ":", "\r\n");
$ftp_port = (int)find_value_with_key($buf, "ftp_port" , ":", "\r\n");
$ftp_id = find_value_with_key($buf, "ftp_id" , ":", "\r\n");
$ftp_pass = find_value_with_key($buf, "ftp_pass" , ":", "\r\n");
$ftp_root_dir = find_value_with_key($buf, "ftp_root_dir" , ":", "\r\n");
$uart_thres_time = (int)find_value_with_key($buf, "uart_thres_time" , ":", "\r\n");
$uart_thres_size = (int)find_value_with_key($buf, "uart_thres_size" , ":", "\r\n");
$ftp_state = 0;
$ftp_cur_fn = ""; // current filename
$ftp_cur_dir = ""; // current directory
$uart_pid = 0;
function system_setup()
{
global $uart_pid;
$envs = envs_load();
$uart_pid = pid_open("/mmap/uart0");
$unpack = envs_unpack_uart(envs_find($envs, 0x10, 0));
$temp = $unpack[5];
pid_ioctl($uart_pid, "set baud $temp");
$temp = $unpack[3];
pid_ioctl($uart_pid, "set parity $temp");
$temp = $unpack[1];
pid_ioctl($uart_pid, "set data $temp");
$temp = $unpack[2];
pid_ioctl($uart_pid, "set stop $temp");
$temp = $unpack[4];
pid_ioctl($uart_pid, "set flowctrl $temp");
st_free_setup(THRES_TIMER_ID, "sec");
}
// change directory, makes directory if it doesn't exist
function change_dir($dir)
{
$retb = ftp_chdir($dir);
if($retb == TRUE) return TRUE;
$dir_list = array();
$dir_list = explode("/", $dir, 10);
$cnt = count($dir_list);
// move to root
$retb = ftp_chdir("/");
if($retb == FALSE) return FALSE;
for($i = 0; $i < $cnt; $i++)
{
if($dir_list[$i] == "") continue;
$retb = ftp_chdir($dir_list[$i]);
if($retb == TRUE) continue;
$ret = ftp_mkdir($dir_list[$i]);
if($ret === FALSE) return FALSE;
else
{
$retb = ftp_chdir($dir_list[$i]);
if($retb == TRUE) continue;
else return FALSE;
}
}
return TRUE;
}
function loop_session()
{
global $ftp_state;
global $ftp_server, $ftp_port;
global $ftp_id, $ftp_pass;
global $ftp_cur_dir;
if($ftp_state == 0)
{
$retb = ftp_connect($ftp_server, $ftp_port, 10);
if($retb == FALSE)
{
echo "ftp - failed to connect to the FTP server.\r\n";
return;
}
else $ftp_state = 1;
}
if($ftp_state == 1)
{
$retb = ftp_login($ftp_id, $ftp_pass);
$ftp_cur_dir = "";
if($retb == FALSE)
{
echo "ftp - login failed.\r\n";
ftp_close();
$ftp_state = 0;
}
$ftp_state = 2;
}
$tcp_state = ftp_cmd_session_state(); // checking ftp tcp session
if($tcp_state == 0) $ftp_state = 0;
}
function loop_uart()
{
global $ftp_cur_dir, $ftp_cur_fn, $ftp_root_dir;
global $uart_thres_size, $uart_thres_time, $uart_pid;
$rbuf = "";
$timer = st_free_get_count(THRES_TIMER_ID);
$rxlen = pid_ioctl($uart_pid, "get rxlen");
if($timer < $uart_thres_time && $rxlen < $uart_thres_size)
return;
else
$len = pid_read($uart_pid, $rbuf);
if($len > 0)
{
echo "$rbuf";
$curr_time = date("Ymd-His");
$today = substr($curr_time, 0, 8);
if($today !== $ftp_cur_dir)
{
change_dir("/$ftp_root_dir");
ftp_mkdir($today);
change_dir("/$ftp_root_dir/$today");
$ftp_cur_dir = $today;
}
$ftp_cur_fn = substr($curr_time, 0, 11) . '.log';
ftp_append($ftp_cur_fn, $rbuf);
st_free_setup(THRES_TIMER_ID, "sec");
}
}
function loop_dns()
{
}
system_setup();
while(1)
{
loop_session();
loop_uart();
loop_dns();
}
?>