質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.34%

  • C

    4951questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

c言語によるXMODEMなどの実装と送受信について

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,256

NEWBIEEBIEE

score 45

RaspberryPI上で下記のコードを動かしたいのですが、全く動きません。
コンパイルは通ってます。
XMODEMをいう規格でファイルの送受信を行いたいのですが、普通のxmodemと違うのは1度送信したデータを2回立て続けに再送するところです。
片方向のデータ送信のみしか許されない状況下での通信を想定しており、
受信側ではチェックサムなどから受信したデータの整合性について多数決で決めています。

terminal でmain関数内にxmodem_receiveを実行するプログラムとxmodem_sendを実行するプログラムをそれぞれtty/USB0とtty/AMA0を指定して実行します。保存先のファイルが開かれますが0バイトのままで、なんの変化もありません。送受信についてはpython の簡単なテキスト送受信プログラムで確認できています。

ちょっとだけでもいいので、受信と送信ができているところがみたいです。

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/types.h> //
#include <sys/stat.h> // 送受信ファイルの状態
#include <sys/mman.h> // メモリ管理


#define X_STX 0x02
#define X_EOT 0x04
#define X_ACK 0x06
#define X_NAK 0x15

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

#define DEBUG 0

struct xmodem_chunk {
    uint8_t start;
    uint8_t blk;
    uint8_t blk_neg;
    uint8_t payload[1024];
    uint16_t crc;
} __attribute__((packed));// メモリとアドレスのアライン(連続させる)

#define CRC_POLY 0x1021

/* 'Safe' write */
int safewrite(int fd, const void *p, size_t want){
    int ret;
    int ret_sum = 0;

    errno = 0;
    while(want){
        ret = write(fd, (uint8_t *)p,want);
        if(ret <= 0) {
            if (errno != EINTR && errno != EAGAIN){
                return -1;
            }
            errno = 0;
            continue;
        }
        want -= ret;
        p = (uint8_t*) p + ret;
        ret_sum += ret;
    }
    return ret_sum;
}

int saferead(int fd, const void *p, size_t want){
    int ret;
    int ret_sum = 0;

    errno = 0;
    while (want){
        ret = read(fd, (uint8_t*)p, want);
        if(ret == 0)
            return -1; /* EOF */
        if(ret <= 0){
            if(errno != EINTR && errno != EAGAIN ) {
                return -1;
            }
            errno = 0;
            continue;
        }
        want -= ret;
        p = (uint8_t*) p + ret;
        ret_sum += ret;
    }
    return ret_sum;
}

static uint16_t crc_update(uint16_t crc_in, int incr)
{
    uint16_t xor = crc_in >> 15;
    uint16_t out = crc_in << 1;

    if(incr)
        out++;

    if(xor)
        out ^= CRC_POLY; // xor 0b1000000100001

    return out;
}

/**
* CRCを計算する(バッファされたデータを一気に計算)
*
* CRC-16-CCITT計算
* 生成多項式=x^16+x^12+x^5+1 (除数
*/
static uint16_t crc16(const uint8_t *data, uint16_t size) // データのチャンク8botづつに対しての処理
{
    uint16_t crc, i;

    for(crc = 0; size > 0; size--, data++) // >>= は複合代入演算子、 a = a >> b;と同じである
        for(i = 0x80; i; i >> 1)
            crc = crc_update(crc, *data & i); // 8ビットデータ本体のチャンクに対して上位4bitがすべて1の場合はCRC_POLYでxor

    for ( i = 0; i < 16; i++)
        crc = crc_update(crc, 0);

    return crc;
}
static uint16_t swap16(uint16_t in) // 16bitの内上位8bit下位8bitの入れ替え
{
    return (in >> 8) | ((in & 0xff) << 8);
}

enum {
    PROTOCOL_XMODEM,
    PROTOCOL_YMODEM,
};

static int xymodem_send(int serial_fd, const char *filename)
{
    size_t len; // ファイルのサイズ
    int ret, fd;
    uint8_t answer;
    struct stat stat;
    const uint8_t *buf; //
    uint8_t eof = X_EOT; // データをすべて送信した際の符号
    struct xmodem_chunk chunk; // ファイルの一部を入れるデータチャンク
    int skip_payload = 0;

    fd = open(filename, O_RDONLY); // ファイルディスクリプタの取得
    if(fd < 0){ // ファイルディスクリプタが0以下ならエラー
        perror("open");
        return -errno;
    }


    fstat(fd, &stat); // ファイルの状態の取得
    len = stat.st_size; // ファイルサイズを取得
    buf = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); //仮想空間の作成 マッピングを行う。 メモリー保護は読み込み可能(PROT_READ)で同じファイルをマッピングしているほかプロセスに対して更新が見えず、更新がマッピング元のファおるに伝わることもない。

    if(!buf){ // mmapでマップ領域のポインタが介されない場合
        perror("mmap");
        return -errno;
    }


    printf("Sending %s ", filename);

    chunk.start = X_STX; //最初のスタートビット

    while (len){
        size_t z = 0;
        int next = 0;
        char status;

        z = min(len, sizeof(chunk.payload)); // ファイルの長さがチャンク以下ならチャンクにその長さで入れる
        memcpy(chunk.payload, buf, z);
        memset(chunk.payload + z, 0xff, sizeof(chunk.payload) - z); //

        chunk.crc = swap16(crc16(chunk.payload, sizeof(chunk.payload)));
        chunk.blk_neg = 0xff - chunk.blk; // 降順でデータに送付するブロック
        int i;
        for(i = 0; i < 3; i++){
            ret = safewrite(serial_fd, &chunk, sizeof(chunk)); // 書き込まれたバイト数を返す
            if(ret != sizeof(chunk)){
                return -errno;
            }
        }

        if(next){
            chunk.blk++;
            len -= z;
            buf += z;
        }
    }
    for(int i = 0; i < 3; i++){
        ret = safewrite(serial_fd, &eof, sizeof(eof));
        if(ret != sizeof(eof))
            return -errno;
    }
    printf("done.\n");
    return 0;
}

static xmodem_receive(int serial_fd, char* filename){
    size_t len;
    int ret;
    size_t retry;
    struct xmodem_chunk chunk;
    struct stat stat;
    int file_size=0;
    FILE *fp;
    size_t ch_size = sizeof(chunk);

    int eof_count=0;


    retry = 3;
        /* バイナリ書き込み読み込みモードでファイルをオープン */
    fp = fopen(filename, "ab+");


    while(1){

        int v_stx_count=0;
        int inv_stx_count=0;
        int garbage_blk_count=0;
        int correct_blk_count=0;
        int garbage_count=0;
        int correct_count=0;
        uint8_t chunk_payload[1024];
        uint8_t garbage_payload[3][1024];
        uint16_t garbage_crc[3];
        int val_payload_crc_pr = 0;
        int val_payload_crc_cr = 0;
        int val_payload_crc_nx = 0;
        int pr_val_crc=0;
        int nx_val_crc=0;

        int val_crc=0;
        size_t z = 0;
        while(retry){

                int next = 0;

                ret = saferead(serial_fd, &chunk, ch_size);
                printf("チャンクを受け取りました");
                z = sizeof(chunk.payload);
                if(chunk.start != X_STX){
                    printf("error STX\n");
                    return 0; // error
                }
                printf("retry part\n");
                if(chunk.crc != swap16(crc16(chunk.payload, sizeof(chunk.payload)))){ // チャンクがごみかどうか
                    if(garbage_count > 1){
                        int i;
                        for(i=0; i < garbage_count; i++){
                            // ごみからあたりを見つける
                            if(chunk.crc == swap16(crc16(garbage_payload[i], sizeof(chunk.payload)))){
                                correct_count++;
                                memcpy(chunk_payload, garbage_payload[i], len);
                            }
                        }
                        if(correct_count < 1){
                            memcpy(garbage_payload[garbage_count], &chunk.payload, len);
                            garbage_count++;
                        }
                        printf("garbage#1\n");
                    }else{
                        // ごみの登録
                        memcpy(garbage_payload[0], &chunk.payload, len);
                        garbage_crc[0] = chunk.crc;
                        garbage_count++;
                        printf("garbage#2\n");
                    }


                }else{
                    printf("correct\n");
                    correct_count++;
                    memcpy(chunk_payload , &chunk.payload, len);
                }
                 retry--;


        }
        safewrite(fp, &chunk_payload, z);


    }
    close(fp);
    return 0;
}

static int open_serial(const char *path, int baud)
{
    int fd;
    struct termios tty;

    fd = open(path, O_RDWR | O_SYNC);
    if(fd < 0) {
        perror("open");
        return -errno;
    }

    memset(&tty, 0, sizeof(tty));
    if(tcgetattr(fd, &tty) != 0) {
        perror("tcgetattr");
        return -errno;
    }

    cfsetospeed(&tty, baud);
    cfsetispeed(&tty, baud);

    tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8-bit
    tty.c_iflag &= ~IGNBRK;             // disable break processing
    tty.c_lflag = 0;                // nosignaling chars, no echo,
    // no canonical processing
    tty.c_oflag = 0;                // no remapping, no delays
    tty.c_cc[VMIN] = 1;             // read doesn't block
    tty.c_cc[VTIME] = 5;                // 0.5 seconds read timeout

    tty.c_iflag &= ~(IXON | IXOFF | IXANY);     // shut off xon/xoff ctrl

    tty.c_cflag |= (CLOCAL | CREAD);        // ignore modem controlsm

    tty.c_cflag &= ~(PARENB | PARODD);      // ignore modem controls,
    // enable reading
    tty.c_cflag &= ~(PARENB | PARODD);      // shut off party
    tty.c_cflag &= ~CSTOPB;
    tty.c_cflag &= ~CRTSCTS;

    if (tcsetattr(fd, TCSANOW, &tty) != 0) {
        perror("tcsetattr");
        return -errno;
    }

    return fd;
}

static void dump_serial(int serial_fd)
{
    char in;

    for (;;) {
        read(serial_fd, &in, sizeof(in));
        printf("%c", in);
        fflush(stdout);
    }
}

int main(int argc, char **argv){
    int ret, serial_fd;

    serial_fd = open_serial("/dev/ttyUSB0", 115200);


    ret = xmodem_receive(serial_fd, "sample.jpg");

}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

0

全く動かないというのがどのような状況なのかが不明です。
printfが所々にありますが、実行するとどのあたりまで想定通りの
出力がありますか?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/01/01 11:02

    返信ありがとうございます。terminal でmain関数内にxmodem_receiveを実行するプログラムとxmodem_sendを実行するプログラムをそれぞれtty/USB0とtty/AMA0を指定して実行します。保存先のファイルが開かれますが0バイトのままで、なんの変化もありません。送受信についてはpython の簡単なテキスト送受信プログラムで確認できています。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.34%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • C

    4951questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。