XbeeApi

Dependents:   XbeeAPI Hybrid_main_FirstEdtion

XbeeApiLib.cpp

Committer:
ark777
Date:
2017-03-13
Revision:
2:30220fb0e20e
Parent:
1:34007fc9235e

File content as of revision 2:30220fb0e20e:

#include "mbed.h"
#include <XbeeApiLib.h>
#include <string>

XbeeApi::XbeeApi(PinName Tx, PinName Rx, int BaudRate, void (*onPacketReceived)(int, char*)) : Xbee(Tx, Rx, BaudRate){
    //Xbee = Serial(Tx, Rx, BaudRate);
    //Xbee.baud(BaudRate);
    
    HopLimit = 0;
    for(int i = 0;i < 4;i++){
        address[i].IsAvailable = false;
    }
    IsTransmitting = false;
    SetInterrupt(true);
    OnPacketReceived = onPacketReceived;
}

void XbeeApi::SetAddress(int Num, unsigned long HIGH, unsigned long LOW){
    for(int i = 0;i < 4;i++){
        address[Num].ADR[3 - i] = reinterpret_cast<char (&)[sizeof(HIGH)]>(HIGH)[i];
        address[Num].ADR[7 - i] = reinterpret_cast<char (&)[sizeof(LOW)]>(LOW)[i];
    }
    address[Num].IsAvailable = false;
    address[Num].sum = 0;
    //EndPacket()でチェックサムを計算する際、宛先アドレスもチェックサムに含まれる
    //宛先アドレスは頻繁に変更されないので、アドレス分の合計だけを先にまとめて計算しておく
    for(int i = 0;i < 8;i++){
        address[Num].sum += address[Num].ADR[i];
    }
    address[Num].ADR16bit[0] = 0x00;
    address[Num].ADR16bit[1] = 0x00;
}

//
//char StartWith: 送信されるデータの最初の1バイトに送信内容のタグとしての役割を持たせている
//これにより、受信側では送信されてきたバイナリデータをどのような形式で読み取ればよいのか判断する
void XbeeApi::StartPacket(char StartWith){
    CheckSum = 0x12;
    buf[17] = StartWith;
    itr = 18;
}

//文字列を送信バッファに書き込む
void XbeeApi::Write(string val){
    int length = val.length();
    if(length < 128 - itr){     //送信バッファの空きよりも文字列が長い場合は何もしない
        for(int i = 0;i < length;i++){
            buf[itr + i] = val.c_str()[i];
        }
        itr += length;
    }else{
        buf[17] = 0x00;
        EndPacket();
    }
}

//int型の数値をバイト列として書き込む
//リトルエンディアン(位の大きい数字ほどバイト列では後ろになる)であることに注意
void XbeeApi::Write(int val){
    if(sizeof(val) < 128 - itr){
        for(int i = 0;i < sizeof(val);i++){
            buf[itr + i] = reinterpret_cast<char (&)[sizeof(val)]>(val)[i];
        }
        itr += sizeof(val);
    }else{
        buf[17] = 0x00;
        EndPacket();
    }
}

//long型の数値をバイト列として書き込む
//リトルエンディアン(位の大きい数字ほどバイト列では後ろになる)であることに注意
void XbeeApi::Write(long long val){
    if(sizeof(val) < 128 - itr){
        for(int i = 0;i < sizeof(val);i++){
            buf[itr + i] = reinterpret_cast<char (&)[sizeof(val)]>(val)[i];
        }
        itr += sizeof(val);
    }else{
        buf[17] = 0x00;
        EndPacket();
    }
}

//double型の数値をバイト列として書き込む
//リトルエンディアン(位の大きい数字ほどバイト列では後ろになる)であることに注意
void XbeeApi::Write(double val){
    if(sizeof(val) < 128 - itr){
        for(int i = 0;i < sizeof(val);i++){
            buf[itr + i] = reinterpret_cast<char (&)[sizeof(val)]>(val)[i];
        }
        itr += sizeof(val);
    }else{
        buf[17] = 0x00;
        EndPacket();
    }
}

//float型の数値をバイト列として書き込む
//リトルエンディアン(位の大きい数字ほどバイト列では後ろになる)であることに注意
void XbeeApi::Write(float val){
    if(sizeof(val) < 128 - itr){
        for(int i = 0;i < sizeof(val);i++){
            buf[itr + i] = reinterpret_cast<char (&)[sizeof(val)]>(val)[i];
        }
        itr += sizeof(val);
    }else{
        buf[17] = 0x00;
        EndPacket();
    }
}

void XbeeApi::EndPacket(){
    if(itr != 0){
        buf[0] = 0x7E;    //パケットの先頭を表す
        buf[1] = (char)(itr - 3) >> 8;
        buf[2] = (char)(itr - 3) & 0x00FF;
        buf[3] = 0x10;
        buf[4] = 0x00;         //ここを01にするとxbeeは送信の成否の判定を行うようになる(クソ重い)
        buf[15] = 0x01;
        buf[16] = 0x01;         //0x01:送りっぱなし
        for(int i = 17;i < itr; i++){
            CheckSum += buf[i];
        }
        BytesToTransmit = itr + 1;
        NextTransmitIndex = BytesToTransmit;
        TransmittingAddrNum = -1;
        itr = 0;
        IsTransmitting = true;
        OnTransmitEnabled();
    }
}

void XbeeApi::OnTransmitEnabled(){
    if(IsTransmitting == true){
        if(NextTransmitIndex < BytesToTransmit){
            WriteWithEsc(buf[NextTransmitIndex]);
            NextTransmitIndex++;
        }else{
            NextTransmitIndex = 0;
            //次の送信先に切り替える
            while(true){
                if(TransmittingAddrNum < 4){
                    TransmittingAddrNum++;
                    if(address[TransmittingAddrNum].IsAvailable == true){
                        //次の送信先に送るためのバッファの準備
                        for(int i = 0; i < 8; i++){
                            buf[i + 5] = address[TransmittingAddrNum].ADR[i];
                        }
                        buf[13] = address[TransmittingAddrNum].ADR16bit[0];
                        buf[14] = address[TransmittingAddrNum].ADR16bit[1];
                        buf[BytesToTransmit - 1] = (char)(0xFF - (CheckSum + address[TransmittingAddrNum].sum + address[TransmittingAddrNum].ADR16bit[0] + address[TransmittingAddrNum].ADR16bit[1]) & 0xFF);
                        Xbee.putc(buf[0]);
                        NextTransmitIndex++;
                        break;
                    }
                }else{
                    //送信完了
                    IsTransmitting = false;
                    break;
                }
            }
        }
    }
}

void XbeeApi::WriteWithEsc(char c){
    if(c == 0x7E){
        Xbee.putc(0x7D);
        Xbee.putc(0x5E);
    }else if(c == 0x7D){
        Xbee.putc(0x7D);
        Xbee.putc(0x5D);
    }else{
        Xbee.putc(c);
    }
}

void XbeeApi::ClearPacket(){
    CheckSum = 0;
    itr = 0;
}


//データ受信時の処理 1バイトごとに呼び出される必要がある
void XbeeApi::OnDataReceived(){
    static bool EscFlag = false;
    static int PacketLength = 0;
    static bool IsReceivingPacket = false;
    
    char message;
    message = Xbee.getc();
    if(message == 0x7E){                    //パケットの先頭バイトは0x7E
        ReceivedBytesCounter = 0;
        PacketLength = 0;
        IsReceivingPacket = true;
        EscFlag = false;
    }
    if(IsReceivingPacket == true){
        if(message == 0x7D){
            EscFlag = true;
        }else{
            if(EscFlag == true){
                RxBuf[ReceivedBytesCounter] = message ^ 0x20;
                EscFlag = false;
            }else{
                RxBuf[ReceivedBytesCounter] = message;
            }
            ReceivedBytesCounter++;
            if(ReceivedBytesCounter >= 256){
                IsReceivingPacket = false;      //受信バッファを溢れる場合は、パケットの読み込みを中止する  次に0x7Eを受信するまで再開されない
            }
        }
        //この時点でReceivedBytesCounterには受信したデータのバイト数が入っている
        //2バイト目と3バイト目はパケットの長さを表している
        if(RxBuf[0] == 0x7E){
            if(ReceivedBytesCounter == 3){                      //パケットの3バイト目までを受信した時点で、パケットの長さを計算できる
                PacketLength = 255 * RxBuf[1] + RxBuf[2];
            }
            if(ReceivedBytesCounter > 3 && ReceivedBytesCounter == PacketLength + 4){   //パケットの長さと同じだけのデータを受信したら
                int sum = 0;
                for(int i = 3; i < ReceivedBytesCounter - 1; i++){       //先頭3バイトとチェックサム以外のすべてのバイトを合計
                    sum += RxBuf[i];
                }
                if((char)(0xFF - sum & 0xFF) == RxBuf[ReceivedBytesCounter - 1]){   //チェックサムが一致するならば
                    //ここにデータ受信時の処理を書く
                    //パケット受信直後なので、多少時間のかかる処理でも問題ないはず
                    if(RxBuf[3] == 0x90){                                           // パケットタイプ0x90は他のXbeeから送信されたデータを表す
                        int DataLength = PacketLength - 12;                         //データ本体の長さは パケットの長さ - 12 ここでいう「パケットの長さ」に、最初の3バイトとチェックサムは含まない
                        OnPacketReceived(DataLength, &RxBuf[15]);                   //パケットタイプ0x90は地上局から送信されたデータが16バイト目 すなわち RxBuf[15] から格納されている
                        //OnPacketReceived(DataLength + 16, RxBuf);   //デバッグ用 パケットすべてを渡す場合
                        if(DataLength == 2 && RxBuf[15] == 0x59 && RxBuf[16] == 0x4D){      //0x594Dが送られて来たら、送信元の64bitアドレスをチェックする
                            for(int i = 0; i < 4; i++){
                                bool IsMatchAddr = true;
                                for(int j = 0; j < 8; j++){
                                    if(address[i].ADR[j] != RxBuf[j + 4]){
                                        IsMatchAddr = false;                                //16bitアドレスを調べる 1つでも一致しなければ終了するし
                                        break;
                                    }
                                }
                                if(IsMatchAddr == true){                                    //0x594Dを送信してきた地上局の64bitアドレスがaddress[i]と一致した場合
                                    address[i].ADR16bit[0] = RxBuf[12];                     //address[i]の16bitアドレスを地上局側に合わせる
                                    address[i].ADR16bit[1] = RxBuf[13];
                                    address[i].IsAvailable = true;
                                    break;
                                }
                            }
                        }
                    }
                }
                IsReceivingPacket = false;
            }
        }
    }
}

void XbeeApi::SetInterrupt(bool val){
    if(val == true){
        Xbee.attach(callback(this, &XbeeApi::OnDataReceived), Serial::RxIrq);
        Xbee.attach(callback(this, &XbeeApi::OnTransmitEnabled), Serial::TxIrq);
    }else{
        Xbee.attach(NULL, Serial::RxIrq);
        Xbee.attach(NULL, Serial::TxIrq);
    }
}