Mongoose_Arduino_RadioHead/src/RH_Serial.cpp

238 lines
4.7 KiB
C++

// RH_Serial.cpp
//
// Copyright (C) 2014 Mike McCauley
// $Id: RH_Serial.cpp,v 1.12 2016/04/04 01:40:12 mikem Exp $
#include <RH_Serial.h>
#if (RH_PLATFORM == RH_PLATFORM_STM32F2)
#else
#include <HardwareSerial.h>
#endif
#include <RHCRC.h>
RH_Serial::RH_Serial(HardwareSerial& serial)
:
_serial(serial),
_rxState(RxStateInitialising)
{
}
HardwareSerial& RH_Serial::serial()
{
return _serial;
}
bool RH_Serial::init()
{
if (!RHGenericDriver::init())
return false;
_rxState = RxStateIdle;
return true;
}
// Call this often
bool RH_Serial::available()
{
while (!_rxBufValid &&_serial.available())
handleRx(_serial.read());
return _rxBufValid;
}
void RH_Serial::waitAvailable()
{
#if (RH_PLATFORM == RH_PLATFORM_UNIX)
// Unix version driver in RHutil/HardwareSerial knows how to wait without polling
while (!available())
_serial.waitAvailable();
#else
RHGenericDriver::waitAvailable();
#endif
}
bool RH_Serial::waitAvailableTimeout(uint16_t timeout)
{
#if (RH_PLATFORM == RH_PLATFORM_UNIX)
// Unix version driver in RHutil/HardwareSerial knows how to wait without polling
unsigned long starttime = millis();
while ((millis() - starttime) < timeout)
{
_serial.waitAvailableTimeout(timeout - (millis() - starttime));
if (available())
return true;
YIELD;
}
return false;
#else
return RHGenericDriver::waitAvailableTimeout(timeout);
#endif
}
void RH_Serial::handleRx(uint8_t ch)
{
// State machine for receiving chars
switch(_rxState)
{
case RxStateIdle:
{
if (ch == DLE)
_rxState = RxStateDLE;
}
break;
case RxStateDLE:
{
if (ch == STX)
{
clearRxBuf();
_rxState = RxStateData;
}
else
_rxState = RxStateIdle;
}
break;
case RxStateData:
{
if (ch == DLE)
_rxState = RxStateEscape;
else
appendRxBuf(ch);
}
break;
case RxStateEscape:
{
if (ch == ETX)
{
// add fcs for DLE, ETX
_rxFcs = RHcrc_ccitt_update(_rxFcs, DLE);
_rxFcs = RHcrc_ccitt_update(_rxFcs, ETX);
_rxState = RxStateWaitFCS1; // End frame
}
else if (ch == DLE)
{
appendRxBuf(ch);
_rxState = RxStateData;
}
else
_rxState = RxStateIdle; // Unexpected
}
break;
case RxStateWaitFCS1:
{
_rxRecdFcs = ch << 8;
_rxState = RxStateWaitFCS2;
}
break;
case RxStateWaitFCS2:
{
_rxRecdFcs |= ch;
_rxState = RxStateIdle;
validateRxBuf();
}
break;
default: // Else some compilers complain
break;
}
}
void RH_Serial::clearRxBuf()
{
_rxBufValid = false;
_rxFcs = 0xffff;
_rxBufLen = 0;
}
void RH_Serial::appendRxBuf(uint8_t ch)
{
if (_rxBufLen < RH_SERIAL_MAX_PAYLOAD_LEN)
{
// Normal data, save and add to FCS
_rxBuf[_rxBufLen++] = ch;
_rxFcs = RHcrc_ccitt_update(_rxFcs, ch);
}
// If the buffer overflows, we dont record the trailing data, and the FCS will be wrong,
// causing the message to be dropped when the FCS is received
}
// Check whether the latest received message is complete and uncorrupted
void RH_Serial::validateRxBuf()
{
if (_rxRecdFcs != _rxFcs)
{
_rxBad++;
return;
}
// Extract the 4 headers
_rxHeaderTo = _rxBuf[0];
_rxHeaderFrom = _rxBuf[1];
_rxHeaderId = _rxBuf[2];
_rxHeaderFlags = _rxBuf[3];
if (_promiscuous ||
_rxHeaderTo == _thisAddress ||
_rxHeaderTo == RH_BROADCAST_ADDRESS)
{
_rxGood++;
_rxBufValid = true;
}
}
bool RH_Serial::recv(uint8_t* buf, uint8_t* len)
{
if (!available())
return false;
if (buf && len)
{
// Skip the 4 headers that are at the beginning of the rxBuf
if (*len > _rxBufLen-RH_SERIAL_HEADER_LEN)
*len = _rxBufLen-RH_SERIAL_HEADER_LEN;
memcpy(buf, _rxBuf+RH_SERIAL_HEADER_LEN, *len);
}
clearRxBuf(); // This message accepted and cleared
return true;
}
// Caution: this may block
bool RH_Serial::send(const uint8_t* data, uint8_t len)
{
_txFcs = 0xffff; // Initial value
_serial.write(DLE); // Not in FCS
_serial.write(STX); // Not in FCS
// First the 4 headers
txData(_txHeaderTo);
txData(_txHeaderFrom);
txData(_txHeaderId);
txData(_txHeaderFlags);
// Now the payload
while (len--)
txData(*data++);
// End of message
_serial.write(DLE);
_txFcs = RHcrc_ccitt_update(_txFcs, DLE);
_serial.write(ETX);
_txFcs = RHcrc_ccitt_update(_txFcs, ETX);
// Now send the calculated FCS for this message
_serial.write((_txFcs >> 8) & 0xff);
_serial.write(_txFcs & 0xff);
return true;
}
void RH_Serial::txData(uint8_t ch)
{
if (ch == DLE) // DLE stuffing required?
_serial.write(DLE); // Not in FCS
_serial.write(ch);
_txFcs = RHcrc_ccitt_update(_txFcs, ch);
}
uint8_t RH_Serial::maxMessageLength()
{
return RH_SERIAL_MAX_MESSAGE_LEN;
}