/** @ingroup MBUSA * 自身のシリアル番号を出力する(起動時メッセージにも利用) * @param pSer 出力先ストリーム */ void vModbOut_MySerial(tsFILE *pSer) { uint8 *q = au8SerOutBuff; S_OCTET(VERSION_MAIN); S_OCTET(VERSION_SUB); S_OCTET(VERSION_VAR); S_BE_DWORD(ToCoNet_u32GetSerial()); SerCmdAscii_Output_AdrCmd(pSer, SERCMD_ADDR_FR_MODULE, SERCMD_ID_INFORM_MODULE_ADDRESS, au8SerOutBuff, q - au8SerOutBuff); }
/** @ingroup MBUSA * フラッシュ設定を出力する。 * ※ 本実装は実験的で、フラッシュのデータ全てに対応していません。 * * @param pSer 出力先ストリーム * @param pConfig 設定構造体 */ void vModbOut_Config(tsFILE *pSer, tsFlashApp *pConfig) { uint8 *q = au8SerOutBuff; S_BE_DWORD(pConfig->u32appid); S_OCTET(pConfig->u8role); S_OCTET(pConfig->u8layer); S_OCTET(pConfig->u8ch); S_BE_DWORD(pConfig->u32chmask); vSerOutput_ModbusAscii(pSer, SERCMD_ADDR_FR_MODULE, SERCMD_ID_INFORM_NETWORK_CONFIG, au8SerOutBuff, q - au8SerOutBuff); }
/** @ingroup MBUSA * ACK/NACK を出力する * @param pSer 出力先ストリーム * @param bAck TRUE:ACK, FALSE:NACK */ void vModbOut_AckNack(tsFILE *pSer, bool_t bAck) { uint8 *q = au8SerOutBuff; S_OCTET(bAck ? 1 : 0); vSerOutput_ModbusAscii(pSer, SERCMD_ADDR_FR_MODULE, bAck ? SERCMD_ID_ACK : SERCMD_ID_NACK, au8SerOutBuff, q - au8SerOutBuff); }
PRSEV_HANDLER_DEF(E_STATE_APP_WAIT_TX, tsEvent *pEv, teEvent eEvent, uint32 u32evarg) { if (eEvent == E_EVENT_NEW_STATE) { // ネットワークの初期化 if (!sAppData.pContextNwk) { // 初回のみ sAppData.sNwkLayerTreeConfig.u8Role = TOCONET_NWK_ROLE_ENDDEVICE; sAppData.pContextNwk = ToCoNet_NwkLyTr_psConfig_MiniNodes(&sAppData.sNwkLayerTreeConfig); if (sAppData.pContextNwk) { ToCoNet_Nwk_bInit(sAppData.pContextNwk); ToCoNet_Nwk_bStart(sAppData.pContextNwk); } else { ToCoNet_Event_SetState(pEv, E_STATE_APP_SLEEP); // スリープ状態へ遷移 return; } } else { // 一度初期化したら RESUME ToCoNet_Nwk_bResume(sAppData.pContextNwk); } uint8 au8Data[12]; uint8* q = au8Data; S_OCTET(sAppData.sSns.u8Batt); S_BE_WORD(sAppData.sSns.u16Adc1); S_BE_WORD(sAppData.sSns.u16Adc2); S_BE_WORD(sObjBME280.i16Temp); S_BE_WORD(sObjBME280.u16Hum); S_BE_WORD(sObjBME280.u16Pres); if ( bSendMessage( au8Data, q-au8Data ) ) { } else { ToCoNet_Event_SetState(pEv, E_STATE_APP_SLEEP); // 送信失敗 } #ifdef LITE2525A vPortSetHi(LED); #else vPortSetLo(LED); #endif V_PRINTF(" FR=%04X", sAppData.u16frame_count); } if (eEvent == E_ORDER_KICK) { // 送信完了イベントが来たのでスリープする ToCoNet_Event_SetState(pEv, E_STATE_APP_SLEEP); // スリープ状態へ遷移 } // タイムアウト if (ToCoNet_Event_u32TickFrNewState(pEv) > 100) { V_PRINTF(LB"! TIME OUT (E_STATE_APP_WAIT_TX)"); ToCoNet_Event_SetState(pEv, E_STATE_APP_SLEEP); // スリープ状態へ遷移 } }
/** @ingroup MASTER * I2C のコマンドを実行して、応答を返します。 * 無線経由ので要求の場合は、応答は送信元へ無線パケットで戻されます。 * アドレスが0xDBの場合は、要求は自身のモジュールで実行された上 UART に応答します。 * * - 入力フォーマット * - OCTET: ネットワークアドレス(宛先,0xDBは自身のモジュールで実行してUARTに出力) * - OCTET: 0x88 * - OCTET: 要求番号 * - OCTET: コマンド (0x1: Write, 0x2: Read, 0x3: Write and Increment, 0x4: Write and Read) * - OCTET: I2Cアドレス * - OCTET: I2Cコマンド * - OCTET: データサイズ (無い時は 0) * - OCTET[N]: データ (データサイズが0のときは、本フィールドは無し) * * - 出力フォーマット * - OCTET: ネットワークアドレス * - OCTET: 0x89 * - OCTET: 要求番号、入力フォーマットの値がコピーされる * - OCTET: コマンド (0x1: Write, 0x2: Read) * - OCTET: 0:FAIL, 1:SUCCESS * - OCTET: データサイズ (無い時は 0) * - OCTET[N]: データ (データサイズが0のときは、本フィールドは無し) * * @param p 入力書式のバイト列 * @param u16len バイト列長 * @param u8AddrSrc 要求元のネットワークアドレス */ void vProcessI2CCommand(uint8 *p, uint16 u16len, uint8 u8AddrSrc) { //uint8 *p_end = p + u16len; uint8 au8OutBuf[256 + 32]; uint8 *q = au8OutBuf; bool_t bOk = TRUE; uint8 n; static volatile uint16 x; // 入力データの解釈 uint8 u8Addr = G_OCTET(); (void) u8Addr; uint8 u8Command = G_OCTET(); if (u8Command != SERCMD_ID_I2C_COMMAND) { return; } uint8 u8ReqNum = G_OCTET(); uint8 u8I2C_Oper = G_OCTET(); uint8 u8I2C_Addr = G_OCTET(); uint8 u8I2C_Cmd = G_OCTET(); uint8 u8DataSize = G_OCTET(); uint8 *pu8data = p; //uint8 *pu8data_end = p + u8DataSize; #if 0 if (pu8data_end != p_end) { DBGOUT(1, "I2CCMD: incorrect data."LB); return; } #endif // 出力用のバッファを用意しておく S_OCTET(sAppData.u8AppLogicalId); S_OCTET(SERCMD_ID_I2C_COMMAND_RESP); S_OCTET(u8ReqNum); S_OCTET(u8I2C_Oper); //ここで q[0] 成功失敗フラグ, q[1] データサイズ, q[2]... データ q[0] = FALSE; q[1] = 0; DBGOUT(1, "I2CCMD: req#=%d Oper=%d Addr=%02x Cmd=%02x Siz=%d"LB, u8ReqNum, u8I2C_Oper, u8I2C_Addr, u8I2C_Cmd, u8DataSize); switch (u8I2C_Oper) { case 1: bOk &= bSMBusWrite(u8I2C_Addr, u8I2C_Cmd, u8DataSize, u8DataSize == 0 ? NULL : pu8data); break; case 2: if (u8DataSize > 0) { bOk &= bSMBusSequentialRead(u8I2C_Addr, u8DataSize, &(q[2])); if (bOk) q[1] = u8DataSize; } else { bOk = FALSE; } break; case 3: for (n = 0; n < u8DataSize; n++) { bOk &= bSMBusWrite(u8I2C_Addr, u8I2C_Cmd + n, 1, &pu8data[n]); for (x = 0; x < 16000; x++) ; //wait (e.g. for memory device) } break; case 4: if (u8DataSize > 0) { bOk &= bSMBusWrite(u8I2C_Addr, u8I2C_Cmd, 0, NULL ); if (bOk) bOk &= bSMBusSequentialRead(u8I2C_Addr, u8DataSize, &(q[2])); if (bOk) q[1] = u8DataSize; } else { bOk = FALSE; } break; #ifdef USE_I2C_ACM1620 case 0x21: // ACM1620 bDraw2LinesLcd_ACM1602((const char *) pu8data, (const char *) (pu8data + 16)); break; #endif #ifdef USE_I2C_AQM0802A case 0x22: // ACM1620 bDraw2LinesLcd_AQM0802A((const char *) pu8data, (const char *) (pu8data + 8)); break; #endif default: DBGOUT(1, "I2CCMD: unknown operation(%d)."LB, u8I2C_Oper); return; } q[0] = bOk; // 成功失敗フラグを書き込む q = q + 2 + q[1]; // ポインタ q を進める(データ末尾+1) if (u8AddrSrc == SERCMD_ADDR_TO_MODULE) { SerCmdAscii_Output_AdrCmd(&sSerStream, u8AddrSrc, au8OutBuf[1], au8OutBuf + 2, q - au8OutBuf - 2); } else { i16TransmitSerMsg(au8OutBuf, q - au8OutBuf, ToCoNet_u32GetSerial(), sAppData.u8AppLogicalId, u8AddrSrc, FALSE, sAppData.u8UartReqNum++); } }
PRSEV_HANDLER_DEF(E_STATE_APP_WAIT_TX, tsEvent *pEv, teEvent eEvent, uint32 u32evarg) { if (eEvent == E_EVENT_NEW_STATE) { // 暗号化鍵の登録 if (IS_APPCONF_OPT_SECURE()) { bool_t bRes = bRegAesKey(sAppData.sFlash.sData.u32EncKey); V_PRINTF(LB "*** Register AES key (%d) ***", bRes); } // ネットワークの初期化 if (!sAppData.pContextNwk) { // 初回のみ sAppData.sNwkLayerTreeConfig.u8Role = TOCONET_NWK_ROLE_ENDDEVICE; sAppData.pContextNwk = ToCoNet_NwkLyTr_psConfig_MiniNodes(&sAppData.sNwkLayerTreeConfig); if (sAppData.pContextNwk) { ToCoNet_Nwk_bInit(sAppData.pContextNwk); ToCoNet_Nwk_bStart(sAppData.pContextNwk); } else { ToCoNet_Event_SetState(pEv, E_STATE_APP_SLEEP); // スリープ状態へ遷移 return; } } else { // 一度初期化したら RESUME ToCoNet_Nwk_bResume(sAppData.pContextNwk); } // 初期化後速やかに送信要求 V_PRINTF(LB"[SNS_COMP/TX]"); sAppData.u16frame_count++; // シリアル番号を更新する tsTxDataApp sTx; memset(&sTx, 0, sizeof(sTx)); // 必ず0クリアしてから使う! uint8 *q = sTx.auData; sTx.u32SrcAddr = ToCoNet_u32GetSerial(); if (IS_APPCONF_OPT_TO_ROUTER()) { // ルータがアプリ中で一度受信して、ルータから親機に再配送 sTx.u32DstAddr = TOCONET_NWK_ADDR_NEIGHBOUR_ABOVE; } else { // ルータがアプリ中では受信せず、単純に中継する sTx.u32DstAddr = TOCONET_NWK_ADDR_PARENT; } // ペイロードの準備 S_OCTET('T'); S_OCTET(sAppData.sFlash.sData.u8id); S_BE_WORD(sAppData.u16frame_count); S_OCTET(PKT_ID_LIS3DH); // パケット識別子 S_OCTET(sAppData.sSns.u8Batt); S_BE_WORD(sAppData.sSns.u16Adc1); S_BE_WORD(sAppData.sSns.u16Adc2); S_BE_WORD(sObjLIS3DH.ai16Result[LIS3DH_IDX_X]); S_BE_WORD(sObjLIS3DH.ai16Result[LIS3DH_IDX_Y]); S_BE_WORD(sObjLIS3DH.ai16Result[LIS3DH_IDX_Z]); /* DIの入力状態を取得 */ uint8 DI_Bitmap = readInput(); S_OCTET( DI_Bitmap ); sTx.u8Len = q - sTx.auData; // パケットのサイズ sTx.u8CbId = sAppData.u16frame_count & 0xFF; // TxEvent で通知される番号、送信先には通知されない sTx.u8Seq = sAppData.u16frame_count & 0xFF; // シーケンス番号(送信先に通知される) sTx.u8Cmd = 0; // 0..7 の値を取る。パケットの種別を分けたい時に使用する //sTx.u8Retry = 0x81; // 強制2回送信 if (IS_APPCONF_OPT_SECURE()) { sTx.bSecurePacket = TRUE; } if (ToCoNet_Nwk_bTx(sAppData.pContextNwk, &sTx)) { V_PRINTF(LB"TxOk"); ToCoNet_Tx_vProcessQueue(); // 送信処理をタイマーを待たずに実行する } else { V_PRINTF(LB"TxFl"); ToCoNet_Event_SetState(pEv, E_STATE_APP_SLEEP); // 送信失敗 } V_PRINTF(" FR=%04X", sAppData.u16frame_count); } if (eEvent == E_ORDER_KICK) { // 送信完了イベントが来たのでスリープする ToCoNet_Event_SetState(pEv, E_STATE_APP_SLEEP); // スリープ状態へ遷移 } // タイムアウト if (ToCoNet_Event_u32TickFrNewState(pEv) > 100) { V_PRINTF(LB"! TIME OUT (E_STATE_APP_WAIT_TX)"); ToCoNet_Event_SetState(pEv, E_STATE_APP_SLEEP); // スリープ状態へ遷移 } }
/** * UART コマンド待ち * @param E_STATE_RUNNING * @param pEv * @param eEvent * @param u32evarg */ PRSEV_HANDLER_DEF(E_STATE_RUNNING, tsEvent *pEv, teEvent eEvent, uint32 u32evarg) { if (eEvent == E_EVENT_NEW_STATE) { uint8 au8msg[16], *q = au8msg; V_PRINTF(LB "[RUNNING]"); V_FLUSH(); // 起動メッセージとしてシリアル番号を出力する S_BE_DWORD(ToCoNet_u32GetSerial()); sSerCmdOut.au8data = au8msg; sSerCmdOut.u16len = q - au8msg; sSerCmdOut.vOutput(&sSerCmdOut, &sSerStream); } else #if 0 if (!bPortRead(DIO_BUTTON)) { // DIO_BUTTON が Hi に戻ったらスリープに戻す ToCoNet_Event_SetState(pEv, E_STATE_APP_SLEEP); } else #endif if (eEvent == E_ORDER_KICK) { // UARTのコマンドが入力されるまで待って、送信 tsSerCmd_Context *pCmd = NULL; if (u32evarg) { pCmd = (void*)u32evarg; } if (pCmd && pCmd->u16len <= 80) { ; // この場合は処理する } else { ToCoNet_Event_SetState(pEv, E_STATE_APP_SLEEP); return; } V_PRINTF(LB"[RUNNING/UARTRECV ln=%d]", pCmd->u16len); sAppData.u16frame_count++; // シリアル番号を更新する tsTxDataApp sTx; memset(&sTx, 0, sizeof(sTx)); // 必ず0クリアしてから使う! uint8 *q = sTx.auData; sTx.u32SrcAddr = ToCoNet_u32GetSerial(); if (IS_APPCONF_OPT_TO_ROUTER()) { // ルータがアプリ中で一度受信して、ルータから親機に再配送 sTx.u32DstAddr = TOCONET_NWK_ADDR_NEIGHBOUR_ABOVE; } else { // ルータがアプリ中では受信せず、単純に中継する sTx.u32DstAddr = TOCONET_NWK_ADDR_PARENT; } // ペイロードの準備 S_OCTET('T'); S_OCTET(sAppData.sFlash.sData.u8id); S_BE_WORD(sAppData.u16frame_count); S_OCTET(PKT_ID_UART); // パケット識別子 S_OCTET(pCmd->u16len); // ペイロードサイズ memcpy(q, pCmd->au8data, pCmd->u16len); // データ部 q += pCmd->u16len; sTx.u8Len = q - sTx.auData; // パケットのサイズ sTx.u8CbId = sAppData.u16frame_count & 0xFF; // TxEvent で通知される番号、送信先には通知されない sTx.u8Seq = sAppData.u16frame_count & 0xFF; // シーケンス番号(送信先に通知される) sTx.u8Cmd = 0; // 0..7 の値を取る。パケットの種別を分けたい時に使用する //sTx.u8Retry = 0x81; // 強制2回送信 if (IS_APPCONF_OPT_SECURE()) { sTx.bSecurePacket = TRUE; } if (ToCoNet_Nwk_bTx(sAppData.pContextNwk, &sTx)) { V_PRINTF(LB"TxOk"); ToCoNet_Tx_vProcessQueue(); // 送信処理をタイマーを待たずに実行する ToCoNet_Event_SetState(pEv, E_STATE_APP_WAIT_TX); } else { V_PRINTF(LB"TxFl"); ToCoNet_Event_SetState(pEv, E_STATE_APP_RETRY); } if (bRetry) { sTx_Retry = sTx; } V_PRINTF(" FR=%04X", sAppData.u16frame_count); } // タイムアウト(期待の時間までにデータが来なかった) if (ToCoNet_Event_u32TickFrNewState(pEv) > sAppData.sFlash.sData.u32Slp) { V_PRINTF(LB"! TIME OUT (E_STATE_RUNNING)"); ToCoNet_Event_SetState(pEv, E_STATE_APP_SLEEP); // スリープ状態へ遷移 } }
/**************************************************************************** * * NAME: cbvMcRxHandler * * DESCRIPTION: * * RETURNS: * ****************************************************************************/ PUBLIC void cbToCoNet_vRxEvent(tsRxDataApp *pRx) { int i; // print coming payload V_PRINTF( LB "[PKT Ad:%04x,Ln:%03d,Seq:%03d,Lq:%03d,Tms:%05d %s\"", pRx->u32SrcAddr, pRx->u8Len, // Actual payload byte: the network layer uses additional 4 bytes. pRx->u8Seq, pRx->u8Lqi, pRx->u32Tick & 0xFFFF, pRx->bSecurePkt ? "Enc " : ""); for (i = 0; i < pRx->u8Len; i++) { if (i < 32) { V_PUTCHAR((pRx->auData[i] >= 0x20 && pRx->auData[i] <= 0x7f) ? pRx->auData[i] : '.'); } else { V_PRINTF( ".."); break; } } V_PRINTF( "\"]"); // 暗号化対応時に平文パケットは受信しない if (IS_APPCONF_OPT_SECURE()) { if (!pRx->bSecurePkt) { V_PRINTF( ".. skipped plain packet."); return; } } // 直接受信したパケットを上位へ転送する // // 直接親機宛(TOCONET_NWK_ADDR_PARENT指定で送信)に向けたパケットはここでは処理されない。 // 本処理はアドレス指定がTOCONET_NWK_ADDR_NEIGHBOUR_ABOVEの場合で、一端中継機が受け取り // その中継機のアドレス、受信時のLQIを含めて親機に伝達する方式である。 if (pRx->auData[0] == 'T') { tsTxDataApp sTx; memset(&sTx, 0, sizeof(sTx)); uint8 *q = sTx.auData; S_OCTET('R'); // 1バイト目に中継機フラグを立てる S_BE_DWORD(pRx->u32SrcAddr); // 子機のアドレスを S_OCTET(pRx->u8Lqi); // 受信したLQI を保存する memcpy(sTx.auData + 6, pRx->auData + 1, pRx->u8Len - 1); // 先頭の1バイトを除いて5バイト先にコピーする q += pRx->u8Len - 1; sTx.u8Len = q - sTx.auData; sTx.u32DstAddr = TOCONET_NWK_ADDR_PARENT; sTx.u32SrcAddr = ToCoNet_u32GetSerial(); // Transmit using Long address sTx.u8Cmd = 0; // data packet. sTx.u8Seq = pRx->u8Seq; sTx.u8CbId = pRx->u8Seq; sTx.u16DelayMax = 300; // 送信開始の遅延を大きめに設定する if (IS_APPCONF_OPT_SECURE()) { sTx.bSecurePacket = TRUE; } ToCoNet_Nwk_bTx(sAppData.pContextNwk, &sTx); } }
/* パケットを送信する状態 */ PRSEV_HANDLER_DEF(E_STATE_RUNNING, tsEvent *pEv, teEvent eEvent, uint32 u32evarg) { if (eEvent == E_EVENT_NEW_STATE) { // ADC の開始 vADC_WaitInit(); vSnsObj_Process(&sAppData.sADC, E_ORDER_KICK); } if (eEvent == E_ORDER_KICK) { V_PRINTF(LB"[E_STATE_RUNNING/SNS_COMP]"); sAppData.u16frame_count++; // シリアル番号を更新する tsTxDataApp sTx; memset(&sTx, 0, sizeof(sTx)); // 必ず0クリアしてから使う! uint8 *q = sTx.auData; sTx.u32SrcAddr = ToCoNet_u32GetSerial(); sTx.u16DelayMin = 0; sTx.u16DelayMax = 0; sTx.u16RetryDur = 0; sTx.u8Retry = 0; if (IS_APPCONF_OPT_TO_ROUTER()) { // ルータがアプリ中で一度受信して、ルータから親機に再配送 sTx.u32DstAddr = TOCONET_NWK_ADDR_NEIGHBOUR_ABOVE; } else { // ルータがアプリ中では受信せず、単純に中継する sTx.u32DstAddr = TOCONET_NWK_ADDR_PARENT; } // ペイロードの準備 S_OCTET('T'); S_OCTET(sAppData.sFlash.sData.u8id); S_BE_WORD(sAppData.u16frame_count); S_OCTET(sAppData.sFlash.sData.u8mode); // パケット識別子 S_OCTET(sAppData.sSns.u8Batt); S_BE_WORD(sAppData.sSns.u16Adc1); S_BE_WORD(sAppData.sSns.u16Adc2); // 立ち上がりで起動 or 立ち下がりで起動 if( sAppData.sFlash.sData.i16param == 1 ){ S_OCTET(0x01); } else { S_OCTET(0x00); } /* DIの入力状態を取得 */ uint8 DI_Bitmap = readInput(); S_OCTET( DI_Bitmap ); sAppData.bDI1_Now_Opened = ((DI_Bitmap & 1) == 0); sTx.u8Len = q - sTx.auData; // パケットのサイズ sTx.u8CbId = sAppData.u16frame_count & 0xFF; // TxEvent で通知される番号、送信先には通知されない sTx.u8Seq = sAppData.u16frame_count & 0xFF; // シーケンス番号(送信先に通知される) sTx.u8Cmd = 0; // 0..7 の値を取る。パケットの種別を分けたい時に使用する //sTx.u8Retry = 0x81; // 強制2回送信 if (IS_APPCONF_OPT_SECURE()) { sTx.bSecurePacket = TRUE; } if (ToCoNet_Nwk_bTx(sAppData.pContextNwk, &sTx)) { V_PRINTF(LB"TxOk"); ToCoNet_Tx_vProcessQueue(); // 送信処理をタイマーを待たずに実行する ToCoNet_Event_SetState(pEv, E_STATE_APP_WAIT_TX); } else { V_PRINTF(LB"TxFl"); ToCoNet_Event_SetState(pEv, E_STATE_APP_SLEEP); } V_PRINTF(" FR=%04X", sAppData.u16frame_count); } }