VoCore2: Serial Port Transfer Small File

When I develop, sometimes wifi or network not working, or ssh can not connect, only way to fix it to use serial port, but serial port do not support transfer file…there are no way to transfer small files, like a simple driver or small application, have to reboot and use uboot to reload everything, and kermit load new firmware to uboot is really slow.

PS: I know somebody can use kermit or sz/lz, but first of all, you need to have them in firmware already, that is a chicken-and-egg problem 🙂

I find default busybox shell(sh) support command “echo -ne”, this is a amazing way to transfer binary data by serial port, and do not need any depends, so I write a simple application, hope this helps.

Usage Example, save TEST.ko to VoCore2 /tmp/TEST.ko: sspdt TEST.ko /dev/ttyACM0

Note: after transfer, remember to run md5sum to check if the data transfer is correct and complete, serial port do not guarantee that. From my test, < 100KB file normally do not have problem, and speed approx 1.5KB/s

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libserialport.h>

// apt install libserialport-dev
// gcc sspdt.c -o sspdt -lserialport
// speed is approx 1.5KB/s

#define min(a, b) ((a) < (b) ? (a) : (b))

uint8_t *alloc_from_file(const char *name, int *size)
{
    FILE *fp = fopen(name, "rb");
    if (fp == NULL)
        return NULL;

    fseek(fp, 0, SEEK_END);

    *size = ftell(fp);
    fseek(fp, 0, SEEK_SET);

    uint8_t *buf = malloc(*size);
    if (buf)
        fread(buf, 1, *size, fp);
    fclose(fp);

    return buf;
}

struct sp_port *ssport_open(const char *name)
{
    struct sp_port *port;
    int baudrate = 115200, res;

    res = sp_get_port_by_name(name, &port);
    if (SP_OK != res)
        return NULL;

    res = sp_open(port, SP_MODE_READ_WRITE);
    if (SP_OK != res) {
        printf("sp_open=%d, %s\n", res, sp_last_error_message());
        return NULL;
    }

    // clear input/output buffer.
    sp_flush(port, SP_BUF_BOTH);

    // gd32f150 supported protocol 115200, 8n1.
    sp_set_baudrate(port, baudrate);
    sp_set_bits(port, 8);
    sp_set_parity(port, SP_PARITY_NONE);
    sp_set_stopbits(port, 1);

    // necessary, or system will drop 0x11 and 0x13.
    sp_set_flowcontrol(port, SP_FLOWCONTROL_NONE);

    return port;
}

void ssport_close(struct sp_port *port)
{
    sp_flush(port, SP_BUF_BOTH);
    sp_close(port);
    sp_free_port(port);
}

int ssport_write(struct sp_port *port, const void *buf, size_t count)
{
    int wbyte;
    wbyte = sp_blocking_write(port, buf, count, 600);
    //print_hex("wr", buf, wbyte);
    return wbyte;
}

int ssport_read(struct sp_port *port, void *buf, size_t count)
{
    int rbyte;
    rbyte = sp_blocking_read(port, buf, count, 600);
    //print_hex("rd", buf, rbyte);
    return rbyte;
}

void process(int cur, int total)
{
    printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
    printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
    printf("%9dB / %9dB [%3.1f%%]", cur, total, (float)cur / total * 100);
    fflush(stdout);
}

void get_file_name(const char *name, char *out)
{
    int i = strlen(name) - 1;
    for (; i >= 0; i--) {
        if (name[i] == '/' || name[i] == '\\')
            break;
    }
    strcpy(out, name + i);
}

int main(int argc, char *argv[])
{
    if (argc != 3) {
        printf("sspdt [input file] [serial port]\r\n");
        printf("send small files to device term by serial port.\r\n");
        return 0;
    }

    int size = 0;
    uint8_t *buf = alloc_from_file(argv[1], &size);
    struct sp_port *sp = ssport_open(argv[2]);

    ssport_write(sp, "\n\n", 2);
    usleep(1000);

    char name[128] = {0};
    get_file_name(argv[1], name);
    printf("file %s will save to device /tmp/%s\r\n", argv[1], name);

    // each time read 16 bytes
    for (int i = 0; i < size; i += 16) {
        char cmd[0x100] = {0};
        sprintf(cmd, "echo -ne '");
        for (int j = 0; j < min(size - i, 16); j++) {
            char tmp[16];
            sprintf(tmp, "\\x%02x", buf[i + j]);
            strcat(cmd, tmp);
        }
        strcat(cmd, "' >> /tmp/");
        strcat(cmd, name);
        strcat(cmd, "\n");

        ssport_write(sp, cmd, strlen(cmd));
        process(i, size);
        usleep(10000);
    }
    process(size, size);
    ssport_close(sp);

    printf("\r\nDONE!\r\n");
    return 1;
}