Testing Modbus RS485 commands

Dependencies:   mbed BufferedSerial

RS485.cpp

Committer:
Allar
Date:
2017-02-27
Revision:
0:dccd2df6a07c

File content as of revision 0:dccd2df6a07c:

#include "RS485.h"
#include <stdarg.h>

typedef unsigned int word;
typedef uint8_t byte;
typedef uint8_t boolean;
typedef void (*voidFuncPtr)(void);
Timer lapse;
    const byte STX = '\2';
    const byte ETX = '\3';

RS485::RS485(PinName tx, PinName rx, PinName dere)
    : BufferedSerial(tx, rx)
{
    return;
}
    

byte RS485::crc8(const byte *addr, byte len)
{
  byte crc = 0;
  while (len--)
    {
    byte inbyte = *addr++;
    for (byte i = 8; i; i--)
      {
      byte mix = (crc ^ inbyte) & 0x01;
      crc >>= 1;
      if (mix)
        crc ^= 0x8C;
      inbyte >>= 1;
      }  // end of for
    }  // end of while
  return crc;
}  // end of crc8

void RS485::sendComplemented( const byte what)
{
  byte c ;
  // first nibble
  c = what >> 4;
  putc((c << 4) | (c ^ 0x0F));

  // second nibble
  c = what & 0x0F;
  putc((c << 4) | (c ^ 0x0F));
}  // end of sendComplemented


void RS485::sendMsg(const byte * data, const byte length)
{
  putc(STX);  // STX
  for (byte i = 0; i < length; i++)
    sendComplemented (data[i]);
  putc(ETX);  // ETX
  sendComplemented(crc8(data, length));
}  // end of sendMsg

// receive a message, maximum "length" bytes, timeout after "timeout" clock_mseconds
// if nothing received, or an error (eg. bad CRC, bad data) return 0
// otherwise, returns length of received data
byte RS485::recvMsg (byte * data,                    // buffer to receive into
              const byte length,              // maximum buffer size
              unsigned long timeout)          // clock_mseconds before timing out
  {

  unsigned long start_time = lapse.read_ms();

  bool have_stx = false;

  // variables below are set when we get an STX
  bool have_etx;
  byte input_pos;
  bool first_nibble;
  byte current_byte;

  while (lapse.read_ms() - start_time < timeout)
    {
    if (readable() > 0)
      {
      byte inByte = getc();

      switch (inByte)
        {

        case STX:   // start of text
          have_stx = true;
          have_etx = false;
          input_pos = 0;
          first_nibble = true;
          start_time = lapse.read_ms();  // reset timeout period
          break;

        case ETX:   // end of text
          have_etx = true;
          break;

        default:
          // wait until packet officially starts
          if (!have_stx)
            break;

          // check byte is in valid form (4 bits followed by 4 bits complemented)
          if ((inByte >> 4) != ((inByte & 0x0F) ^ 0x0F) )
            return 0;  // bad character

          // convert back
          inByte >>= 4;

          // high-order nibble?
          if (first_nibble)
            {
            current_byte = inByte;
            first_nibble = false;
            break;
            }  // end of first nibble

          // low-order nibble
          current_byte <<= 4;
          current_byte |= inByte;
          first_nibble = true;

          // if we have the ETX this must be the CRC
          if (have_etx)
            {
            if (crc8 (data, input_pos) != current_byte)
              return 0;  // bad crc
            return input_pos;  // return received length
            }  // end if have ETX already

          // keep adding if not full
          if (input_pos < length)
            data [input_pos++] = current_byte;
          else
            return 0;  // overflow
          break;

        }  // end of switch
      }  // end of incoming data
    } // end of while not timed out

  return 0;  // timeout
} // end of recvMsg