From ff6825b8127677eaa5b58b0643836228650b80d9 Mon Sep 17 00:00:00 2001 From: Fuminori OKUHARA Date: Mon, 28 Feb 2022 17:42:20 +0900 Subject: [PATCH] =?UTF-8?q?sipf-std-client=20v0.4.0=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - v0.4.0以降の場合認証モード設定をスキップ - ファイル送信対応 - ButtonA: TX, ButtonB: RX, ButtonC: FILE PUTに再割当て - XMODEMライブラリ追加 - $$FPUTコマンド送信を追加 --- sipf-std-m5stack/sipf-std-m5stack.ino | 73 +++--- sipf-std-m5stack/sipf_client.cpp | 102 +++++++- sipf-std-m5stack/sipf_client.h | 4 +- sipf-std-m5stack/xmodem.c | 327 ++++++++++++++++++++++++++ sipf-std-m5stack/xmodem.h | 50 ++++ sipf-std-m5stack/xmodem_arduino.cpp | 81 +++++++ 6 files changed, 605 insertions(+), 32 deletions(-) create mode 100644 sipf-std-m5stack/xmodem.c create mode 100644 sipf-std-m5stack/xmodem.h create mode 100644 sipf-std-m5stack/xmodem_arduino.cpp diff --git a/sipf-std-m5stack/sipf-std-m5stack.ino b/sipf-std-m5stack/sipf-std-m5stack.ino index 4fc4140..a6fc5e9 100644 --- a/sipf-std-m5stack/sipf-std-m5stack.ino +++ b/sipf-std-m5stack/sipf-std-m5stack.ino @@ -29,7 +29,7 @@ * SIPF接続情報 */ static uint8_t buff[256]; -static uint32_t cnt_btn1, cnt_btn2; +static uint32_t cnt_btn1; static int resetSipfModule() { @@ -100,11 +100,16 @@ static void drawButton(uint8_t button, uint32_t value) M5.Lcd.setTextColor(TFT_BLACK, 0xfaae); M5.Lcd.setCursor(40 + (95 * button), 210); - if (button != 2) { + switch (button) { + case 0: // TX M5.Lcd.printf("%4d", value); - } else { - //受信ボタン + break; + case 1: // RX M5.Lcd.printf(" RX "); + break; + case 2: // FILE PUT + M5.Lcd.printf("FILE"); + break; } } @@ -185,16 +190,19 @@ void setup() { return; } - if (SipfGetFwVersion() != 0) { + uint32_t fw_version; + if (SipfGetFwVersion(&fw_version) != 0) { M5.Lcd.printf("SipfGetFwVersion(): FAILED\r\n"); } - M5.Lcd.printf("Setting auth mode..."); - if (SipfSetAuthMode(0x01) == 0) { - M5.Lcd.printf(" OK\n"); - } else { - M5.Lcd.printf(" NG\n"); - return; + if (fw_version < 0x000400) { + M5.Lcd.printf("Setting auth mode..."); + if (SipfSetAuthMode(0x01) == 0) { + M5.Lcd.printf(" OK\n"); + } else { + M5.Lcd.printf(" NG\n"); + return; + } } #ifdef ENABLE_GNSS M5.Lcd.printf("Enable GNSS.."); @@ -208,10 +216,9 @@ void setup() { drawResultWindow(); cnt_btn1 = 0; - cnt_btn2 = 0; drawButton(0, cnt_btn1); - drawButton(1, cnt_btn2); + drawButton(1, 0); drawButton(2, 0); Serial.println("+++ Ready +++"); @@ -263,25 +270,10 @@ void loop() { } } - /* `TX2'ボタンを押した */ - if (M5.BtnB.wasPressed()) { - cnt_btn2++; - drawResultWindow(); - M5.Lcd.printf("ButtonB pushed: TX(tag_id=0x02 value=%d)\n", cnt_btn2); - memset(buff, 0, sizeof(buff)); - int ret = SipfCmdTx(0x02, OBJ_TYPE_UINT32, (uint8_t*)&cnt_btn2, 4, buff); - if (ret == 0) { - M5.Lcd.printf("OK\nOTID: %s\n", buff); - drawButton(1, cnt_btn2); - } else { - M5.Lcd.printf("NG: %d\n", ret); - } - } - /* `RX'ボタンを押した */ - if (M5.BtnC.wasPressed()) { + if (M5.BtnB.wasPressed()) { drawResultWindow(); - M5.Lcd.printf("ButtonC pushed: RX request.\n"); + M5.Lcd.printf("ButtonB pushed: RX request.\n"); memset(buff, 0, sizeof(buff)); static SipfObjObject objs[16]; @@ -376,5 +368,26 @@ void loop() { } } + /* `FILE'ボタンを押した */ + if (M5.BtnC.wasPressed()) { + drawResultWindow(); + M5.Lcd.printf("ButtonC pushed: FILE PUT request.\n"); + //テキストファイルを用意 + int len = sprintf((char *)buff, "millis(): 0x%08x\r\n", millis()); + len += sprintf((char *)&buff[len], "Tx count: %d\r\n", cnt_btn1); + for (int i = 0; i < (sizeof(buff) - len); i++) { + buff[len+i] = (uint8_t)(i % 0x5f) + 0x20; + } + + M5.Lcd.printf("\n\n%s\n\n", (char *)buff); + + int ret = SipfCmdFput("FPUT_SAMPLE_M5.txt", buff, sizeof(buff)); + if (ret == 0) { + M5.Lcd.printf("OK\n"); + } else { + M5.Lcd.printf("NG: %d\n", ret); + } + } + M5.update(); } diff --git a/sipf-std-m5stack/sipf_client.cpp b/sipf-std-m5stack/sipf_client.cpp index 865ad64..5cb6388 100644 --- a/sipf-std-m5stack/sipf_client.cpp +++ b/sipf-std-m5stack/sipf_client.cpp @@ -5,9 +5,12 @@ */ #include #include "sipf_client.h" +#include "xmodem.h" #include #include +#define FPUT_RETRY_MAX (3) + static char cmd[512]; static uint32_t fw_version; @@ -219,7 +222,7 @@ int SipfSetAuthInfo(char *user_name, char *password) /** * Fwバージョンを取得 */ -int SipfGetFwVersion(void) +int SipfGetFwVersion(uint32_t *version) { uint8_t v = 0; if (sipfSendR(0xf1, &v) != 0) { @@ -239,6 +242,10 @@ int SipfGetFwVersion(void) } fw_version |= (uint32_t)v << 8; // RELEASE上位 + if (version) { + *version = fw_version; + } + return 0; } @@ -699,3 +706,96 @@ int SipfCmdRx(uint8_t *otid, uint64_t *user_send_datetime_ms, uint64_t *sipf_rec } } } + +/** + * $$FPUT送信 + */ +static int sipfCmdFputWaitNg(void) +{ + int ret; + for (;;) { + ret = SipfUtilReadLine((uint8_t*)cmd, sizeof(cmd), TMOUT_CHAR); + if (ret == -3) { + //タイムアウト + return -3; + } + if (memcmp(cmd, "NG", 2) == 0) { + //OK + break; + } + } + return 0; +} +static uint8_t buf_xmodem_buff[XMODEM_SZ_BLOCK]; +int SipfCmdFput(char *file_id, uint8_t *file_body, size_t sz_file) +{ + int len, ret; + //UART受信バッファを読み捨てる + SipfClientFlushReadBuff(); + // $$FPUTコマンド送信 + len = sprintf(cmd, "$$FPUT %s %08X\r\n", file_id, sz_file); + ret = Serial2.write((uint8_t*)cmd, len); + + // XMODEM開始 + XmodemBegin(); + // 送信要求待ち(タイムアウト30秒) + XmodemSendRet xret = XmodemSendWaitRequest(30000); + switch (xret) { + case XMODEM_SEND_RET_OK: + break; + case XMODEM_SEND_RET_CANCELED: + default: + // NG待ち + sipfCmdFputWaitNg(); + return xret; + } + // ブロック送信 + uint8_t fget_bn = 1; + size_t sz_block; + for (int idx = 0; idx < sz_file; idx += XMODEM_SZ_BLOCK) { + for (int i = 0; i < FPUT_RETRY_MAX; i++) { + if ((sz_file - idx) < XMODEM_SZ_BLOCK) { + sz_block = len % XMODEM_SZ_BLOCK; + } else { + sz_block = XMODEM_SZ_BLOCK; + } + xret = XmodemSendBlock(&fget_bn, &file_body[idx], sz_block, 10000); //タイムアウト10秒 + switch (xret) { + case XMODEM_SEND_RET_OK: + goto next_block; + case XMODEM_SEND_RET_CANCELED: + // NG待ち + sipfCmdFputWaitNg(); + return xret; + case XMODEM_SEND_RET_RETRY: + // 同じブロックを再送 + continue; + case XMODEM_SEND_RET_TIMEOUT: + case XMODEM_SEND_RET_FAILED: + // NG待ち + sipfCmdFputWaitNg(); + return xret; + } + break; + } +next_block: + fget_bn++; // ブロック番号を加算 + } + + // XMODEM転送終了 + XmodemSendEnd(500); + + // $$FGETコマンドの応答を見る + for (;;) { + ret = SipfUtilReadLine((uint8_t*)cmd, sizeof(cmd), TMOUT_CMD); + if (ret == -3) { + // タイムアウト + return -3; + } + if (memcmp(cmd, "OK", 2) == 0) { + // OK + break; + } + } + return 0; +} diff --git a/sipf-std-m5stack/sipf_client.h b/sipf-std-m5stack/sipf_client.h index 40c894d..ef327c7 100644 --- a/sipf-std-m5stack/sipf_client.h +++ b/sipf-std-m5stack/sipf_client.h @@ -69,11 +69,13 @@ typedef struct { int SipfSetAuthMode(uint8_t mode); int SipfSetAuthInfo(char *user_name, char *password); -int SipfGetFwVersion(void); +int SipfGetFwVersion(uint32_t *version); int SipfCmdTx(uint8_t tag_id, SipfObjTypeId type, uint8_t *value, uint8_t value_len, uint8_t *otid); int SipfCmdRx(uint8_t *otid, uint64_t *user_send_datetime_ms, uint64_t *sipf_recv_datetime_ms, uint8_t *remain, uint8_t *obj_cnt, SipfObjObject *obj_list, uint8_t obj_list_sz); +int SipfCmdFput(char *file_id, uint8_t *file_body, size_t sz_file); + int SipfUtilReadLine(uint8_t *buff, int buff_len, int timeout_ms); void SipfClientFlushReadBuff(void); diff --git a/sipf-std-m5stack/xmodem.c b/sipf-std-m5stack/xmodem.c new file mode 100644 index 0000000..8f19776 --- /dev/null +++ b/sipf-std-m5stack/xmodem.c @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2022 SAKURA internet Inc. + * + * SPDX-License-Identifier: MIT + */ +#include +#include +#include + +#include "xmodem.h" + +#define XMODEM_BLOCK_BN(b) b[1] +#define XMODEM_BLOCK_BNC(b) b[2] +#define XMODEM_BLOCK_DATA_P(b) &b[3] +#define XMODEM_BLOCK_SUM(b) b[131] + +#define LOG_DBG(...) +#define LOG_INF(...) +#define LOG_ERR(...) + +#define LOG_HEXDUMP_DBG(...) +#define LOG_HEXDUMP_INF(...) + +extern int XmodemGetByte(uint8_t *b); +extern int XmodemGetByteTimeout(uint8_t *b, uint32_t timeout); +extern int XmodemPutByte(uint8_t b); +extern int XmodemPut(uint8_t *buff, int sz); +extern void XmodemDelay(uint32_t delay); + +static int xmodem_block_validation(uint8_t *block, uint8_t bn) +{ + LOG_INF("latest bn: %02x, bn: %02x, bnc: %02x", bn, XMODEM_BLOCK_BN(block), XMODEM_BLOCK_BNC(block)); + // BNチェック + if ((XMODEM_BLOCK_BN(block) + XMODEM_BLOCK_BNC(block)) != 0xff) { + // BNとBNCが矛盾してる + LOG_ERR("BN, BNC miss match."); + return -1; + } + if (bn == XMODEM_BLOCK_BN(block)) { + // BNが重複してる + LOG_ERR("Dup BN: %d, %d.", bn, XMODEM_BLOCK_BN(block)); + return -2; + } + if ((uint8_t)(bn + 1) != XMODEM_BLOCK_BN(block)) { + // BNが連続してない + LOG_ERR("Skip BN: %d, %d", bn, XMODEM_BLOCK_BN(block)); + return -1; + } + + // サムチェック + uint8_t *d = XMODEM_BLOCK_DATA_P(block); + uint8_t s = 0; + for (int i = 0; i < 128; i++) { + s = s + d[i]; + } + if (s == XMODEM_BLOCK_SUM(block)) { + LOG_DBG("BN: %d", XMODEM_BLOCK_BN(block)); + return XMODEM_BLOCK_BN(block); + } else { + // サムが一致しない + LOG_ERR("SUM miss match. s=%02x sum=%02x", s, XMODEM_BLOCK_SUM(block)); + return -1; + } +} + +uint8_t *xmodem_data(uint8_t *block) +{ + return &block[4]; +} + +static int xmodemSendAck(void) +{ + // ACK送信 + if (XmodemPutByte(0x06) != 0) { + // 失敗しちゃった + return -1; + } + return 0; +} + +static int xmodemSendNak(void) +{ + // NAKを送信 + if (XmodemPutByte(0x15) != 0) { + // 失敗しちゃった + return -1; + } + return 0; +} + +void XmodemBegin(void) +{ + uint8_t b; + while (XmodemGetByte(&b) == 0); +} + +void XmodemEnd(void) +{ + +} + +/** + * 受信開始 + */ +int XmodemReceiveStart(void) +{ + return xmodemSendNak(); +} + +/** + * 次ブロック要求 + */ +int XmodemReceiveReqNextBlock(void) +{ + return xmodemSendAck(); +} + +/** + * 再送要求 + */ +int XmodemReceiveReqCurrentBlock(void) +{ + return xmodemSendNak(); +} + +/** + * ブロック受信 + * [in/out]bn: 受信済みブロックのBlock number + * [out]block: 受信したブロック + * [in]time_out:受信タイムアウト + * return: + */ +XmodemRecvRet XmodemReceiveBlock(uint8_t *bn, uint8_t *block, int time_out) +{ + uint8_t b; + int ret; + + ret = XmodemGetByteTimeout(&b, time_out); + if (ret == -3) { + LOG_INF("UartBrokerGetByteTm() timeout."); + return XMODEM_RECV_RET_RETRY; + } else if (ret != 0) { + LOG_ERR("UartBrokerGetByteTm() failed: %d", ret); + return ret; + } + + uint8_t idx_block = 0; + + switch (b) { + case 0x01: // SOH + // ブロック開始 + block[idx_block++] = 0x01; + break; + case 0x04: // EOT + // 転送終了 + // ACK送信 + if (xmodemSendAck() != 0) { + // 失敗しちゃった + return XMODEM_RECV_RET_FAILED; + } + return XMODEM_RECV_RET_FINISHED; + case 0x18: // CAN + // 中断要求 + return XMODEM_RECV_RET_CANCELED; + } + + // ブロックの残り131Byteを受信する + for (int i = 0; i < 131; i++) { + //キャラ間タイムアウト100[ms]で受信 + ret = XmodemGetByteTimeout(&b, 100); + if (ret == 0) { + // 受信できた + block[idx_block++] = b; + } else { + // 失敗 + LOG_ERR("UartBrokerGetByteTm() failed: %d", ret); + return ret; + } + } + + // ブロックの正当性チェック + int bn_recv = xmodem_block_validation(block, *bn); + if (bn_recv == -1) { + // チェックサムとか間違ってたから再送要求 + return XMODEM_RECV_RET_RETRY; + } else if (bn_recv == -2) { + // 重複してた + return XMODEM_RECV_RET_DUP; + } + + // ブロックが正しい + *bn = bn_recv; // BNを更新 + return XMODEM_RECV_RET_OK; +} + +/** + * 転送をやめる + */ +int XmodemTransmitCancel(void) +{ + // CANを送信 + if (XmodemPutByte(0x18) != 0) { + // 送信失敗 + return -1; + } + return 0; +} + +/** + * 送信要求待ち + */ +XmodemSendRet XmodemSendWaitRequest(int time_out) +{ + int ret; + uint8_t b; + + for (int i = 0; i < 10; i++) { + ret = XmodemGetByteTimeout(&b, time_out / 10); + // timeoutとか見る + if (ret < 0) { + if (ret == -3) { + LOG_INF("UartBrokerByteTm() timeout."); + continue; + } + return XMODEM_SEND_RET_FAILED; + } + + switch (b) { + case 0x15: // NAK(送信要求) + return XMODEM_SEND_RET_OK; + break; + case 0x18: // CAN(キャンセル) + return XMODEM_SEND_RET_CANCELED; + break; + default: // 想定外のやつ + LOG_INF("Invalid response: %02x", b); + while (XmodemGetByte(&b) == 0); //ゴミ捨て + XmodemDelay(time_out / 10); + break; + } + } + LOG_ERR("XmodemSendWaitRequest() retry over."); + return XMODEM_SEND_RET_FAILED; +} + +/** + * 送信完了 + */ +XmodemSendRet XmodemSendEnd(int time_out) +{ + int ret; + // EOTを送る + ret = XmodemPutByte(0x04); + if (ret < 0) { + return XMODEM_SEND_RET_FAILED; + } + + // ACKを待つ + uint8_t b; + ret = XmodemGetByteTimeout(&b, time_out); + if (ret < 0) { + if (ret == -3) { + // タイムアウト + return XMODEM_SEND_RET_TIMEOUT; + } + return XMODEM_SEND_RET_FAILED; + } + + if (b == 0x06) { + // ACK + return XMODEM_SEND_RET_OK; + } else { + return XMODEM_SEND_RET_FAILED; + } +} + +/** + * ブロック送信 + */ +XmodemSendRet XmodemSendBlock(uint8_t *bn, uint8_t *payload, int sz_payload, int time_out) +{ + int ret; + static uint8_t block[132]; + + if (sz_payload > 128) { + return XMODEM_SEND_RET_FAILED; + } + + memset(block, 0x1a, sizeof(block)); // 先にパディングのEOFで埋めておく + + block[0] = 0x01; // SOH + block[1] = *bn; // BN + block[2] = ~*bn; // BNC + memcpy(&block[3], payload, sz_payload); // DATA + block[131] = 0; // SUM + for (int i = 3; i < 131; i++) { + block[131] += block[i]; + } + + ret = XmodemPut(block, 132); + if (ret < 0) { + return XMODEM_SEND_RET_FAILED; + } + + // 応答を待つ + uint8_t b; + ret = XmodemGetByteTimeout(&b, time_out); + if (ret == -3) { + LOG_ERR("XmodemGetByteTimeout() timeout."); + return XMODEM_SEND_RET_TIMEOUT; + } else if (ret < 0) { + LOG_ERR("XmodemGetByteTimeout() failed."); + return XMODEM_SEND_RET_FAILED; + } + + LOG_DBG("Received: %02x", b); + if (b == 0x06) { // ACK + return XMODEM_SEND_RET_OK; + } else if (b == 0x15) { // NAK + return XMODEM_SEND_RET_RETRY; + } else if (b == 0x18) { // CAN + return XMODEM_SEND_RET_CANCELED; + } else { + // 想定外のなにか来た + return XMODEM_SEND_RET_FAILED; + } +} diff --git a/sipf-std-m5stack/xmodem.h b/sipf-std-m5stack/xmodem.h new file mode 100644 index 0000000..a137748 --- /dev/null +++ b/sipf-std-m5stack/xmodem.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2022 SAKURA internet Inc. + * + * SPDX-License-Identifier: MIT + */ +#ifndef _XMODEM_H_ +#define _XMODEM_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define XMODEM_SZ_BLOCK (128) + +typedef enum xmodem_recv_ret { + XMODEM_RECV_RET_OK, + XMODEM_RECV_RET_FINISHED, + XMODEM_RECV_RET_CANCELED, + XMODEM_RECV_RET_RETRY, + XMODEM_RECV_RET_DUP, + XMODEM_RECV_RET_FAILED = -1, + XMODEM_RECV_RET_TIMEOUT = -2, +} XmodemRecvRet; + +typedef enum xmodem_send_ret { + XMODEM_SEND_RET_OK, + XMODEM_SEND_RET_RETRY, + XMODEM_SEND_RET_CANCELED, + XMODEM_SEND_RET_FAILED = -1, + XMODEM_SEND_RET_TIMEOUT = -2, +} XmodemSendRet; + +void XmodemBegin(void); +void XmodemEnd(void); + +int XmodemTransmitCancel(void); + +int XmodemReceiveStart(void); +XmodemRecvRet XmodemReceiveBlock(uint8_t *bn, uint8_t *block, int time_out); +int XmodemReceiveReqNextBlock(void); +int XmodemReceiveReqCurrentBlock(void); + +XmodemSendRet XmodemSendWaitRequest(int time_out); +XmodemSendRet XmodemSendEnd(int time_out); +XmodemSendRet XmodemSendBlock(uint8_t *bn, uint8_t *payload, int sz_payload, int time_out); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/sipf-std-m5stack/xmodem_arduino.cpp b/sipf-std-m5stack/xmodem_arduino.cpp new file mode 100644 index 0000000..26d8e12 --- /dev/null +++ b/sipf-std-m5stack/xmodem_arduino.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2022 SAKURA internet Inc. + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +extern "C" { + +int XmodemGetByte(uint8_t *b) +{ + int len = Serial2.available(); + if (len == 0) { + return -1; //EMPTY + } + + int ret = Serial2.read(); + if (ret < 0) { + return -1; //ERROR + } + + *b = (uint8_t)ret; + + return 0; +} + +int XmodemGetByteTimeout(uint8_t *b, uint32_t timeout) +{ + int ret, len; + uint32_t t_recved, t_now; + + t_recved = millis(); + for (;;) { + t_now = millis(); + //タイムアウト判定 + if ((int32_t)((t_recved + timeout) - t_now) < 0) { + //タイムアウト + return -3; + } + + len = Serial2.available(); + if (len > 0) { + ret = Serial2.read(); + if (ret < 0) { + return -1; // ERROR + } + *b = (uint8_t)ret; + return 0; // OK + } + } +} + +int XmodemPutByte(uint8_t b) +{ + int ret; + ret = Serial2.write(b); + if (ret < 0) { + return -1; + } + return 0; +} + +int XmodemPut(uint8_t *buff, int sz) +{ + int ret; + ret = Serial2.write(buff, sz); + if (ret < 0) { + return -1; + } + return 0; +} + +void XmodemDelay(uint32_t d) +{ + delay(d); +} + +} +