This is a SLIP interface for the STM32F446RE Nucleo Board. It is designed to work specifically with the esp-link software for the ESP8266. The program is an example of a rest command.

Dependencies:   mbed DHT Matrix

STMClient.cpp

Committer:
ShaneKirkbride
Date:
2016-10-10
Revision:
12:0df73cbe5cbf
Parent:
8:6a3b7c5d9ba7

File content as of revision 12:0df73cbe5cbf:

#include "STMClient.h"
#include "millis.h"

#define xstr(s) str(s)
#define str(s) #s

#define SLIP_END  0300        // indicates end of packet
#define SLIP_ESC  0333        // indicates byte stuffing
#define SLIP_ESC_END  0334    // ESC ESC_END means END data byte
#define SLIP_ESC_ESC  0335    // ESC ESC_ESC means ESC data byte

//===== Input

// Process a received SLIP message
STMClientPacket* STMClient::protoCompletedCb(void) {
  // the packet starts with a STMClientPacket
  STMClientPacket* packet = (STMClientPacket*)_proto.buf;
  if (_debugEn) {
   // _debug->printf("STMC: got %i @ %i:\n\r 0x%x\n\r 0x%x\n\r 0x%x\n\r", _proto.dataLen,(uint32_t)_proto.buf, packet->cmd, packet->value, packet->argc);

    for (uint16_t i=8; i<_proto.dataLen; i++) 
    {
    //  _debug->printf("%x", *(uint8_t*)(_proto.buf+i));
    }
    //_debug->printf("\n\r");
  }

  // verify CRC
  uint16_t crc = crc16Data(_proto.buf, _proto.dataLen-2, 0);
  //_debug->printf("CRC: %i\n\r",crc);
  //wait(0.5);
  uint16_t resp_crc = *(uint16_t*)(_proto.buf+_proto.dataLen-2);
  //_debug->printf("resp_crc: %i\n\r",resp_crc);
  if (crc != resp_crc) {
    DBG("STMC: Invalid CRC\n\r");
    wait(0.25);    
    return NULL; //maybe the CRC isn't getting calculated correctly...
  }

  // dispatch based on command
  if (packet->cmd == CMD_RESP_V) {
    // value response
    _debug->printf("RESP_V: 0x%x \n\r",packet->value);

    return packet;
  } else if (packet->cmd == CMD_RESP_CB) {
    FP<void, void*> *fp;
    // callback reponse
    _debug->printf("RESP_CB: 0x%x 0x%x \n\r", packet->value, packet->argc);

    fp = (FP<void, void*>*)packet->value;
    if (fp->attached()) {
      STMClientResponse resp(packet);
      (*fp)(&resp);
    }
    return NULL;
  } else {
    // command (NOT IMPLEMENTED)
    _debug->printf("CMD 0x%x Value 0x%x ??\n\r", packet->cmd, packet->value);
    return NULL;
  }
}

// Read all characters available on the serial input and process any messages that arrive, but
// stop if a non-callback response comes in
STMClientPacket *STMClient::Process() {
 int value;
  while (_serial->readable()) {
    value = _serial->getc();
    if (value == SLIP_ESC) {
      _proto.isEsc = 1;
    } else if (value == SLIP_END) {
      STMClientPacket *packet = _proto.dataLen >= 8 ? protoCompletedCb() : 0;
      _proto.dataLen = 0;
      _proto.isEsc = 0;
      if (packet != NULL) return packet;
    } else {
      if (_proto.isEsc) {
        if (value == SLIP_ESC_END) value = SLIP_END;
        if (value == SLIP_ESC_ESC) value = SLIP_ESC;
        _proto.isEsc = 0;
      }
      if (_proto.dataLen < _proto.bufSize) {
        _proto.buf[_proto.dataLen++] = value;
      }
    }
  }
  return NULL;
}

//===== Output

// Write a byte to the output stream and perform SLIP escaping
void STMClient::write(uint8_t data) {
  switch (data) {
  case SLIP_END:
    _serial->putc(SLIP_ESC);
    _serial->putc(SLIP_ESC_END);
    break;
  case SLIP_ESC:
    _serial->putc(SLIP_ESC);
    _serial->putc(SLIP_ESC_ESC);
    break;
  default:
    _serial->putc(data);
  }
}

// Write some bytes to the output stream
void STMClient::write(void* data, uint16_t len) {
  uint8_t *d = (uint8_t*)data;
  //_debug->printf("Writing: 0x%x\n\r", (const char*)data);
  while (len--)
  {
    write(*d++);
  }
}

// Start a request. cmd=command, value=address of callback pointer or first arg,
// argc=additional argument count
void STMClient::Request(uint16_t cmd, uint32_t value, uint16_t argc) {
  //_debug->printf("Starting a request...\n\r");
  wait(0.25);
  crc = 0;
  _serial->putc(SLIP_END);
    
  write(&cmd, 2);
  crc = crc16Data((unsigned const char*)&cmd, 2, crc);

  write(&argc, 2);
  crc = crc16Data((unsigned const char*)&argc, 2, crc);

  write(&value, 4);
  crc = crc16Data((unsigned const char*)&value, 4, crc);
}

// Append a block of data as an argument to the request
void STMClient::Request(const void* data, uint16_t len) {
  uint8_t *d = (uint8_t*)data;

  // write the length
  write(&len, 2);
  crc = crc16Data((unsigned const char*)&len, 2, crc);

  // output the data
  for (uint16_t l=len; l>0; l--) {
    write(*d);
    crc = crc16Add(*d, crc);
    d++;
  }

  // output padding
  uint16_t pad = (4-(len&3))&3;
  uint8_t temp = 0;
  while (pad--) {
    write(temp);
    crc = crc16Add(temp, crc);
  }
}
/*
// Append a block of data located in flash as an argument to the request
void STMClient::Request(const __FlashStringHelper* data, uint16_t len) {
  // write the length
  write(&len, 2);
  crc = crc16Data((unsigned const char*)&len, 2, crc);

  // output the data
  PGM_P p = reinterpret_cast<PGM_P>(data);
  for (uint16_t l=len; l>0; l--) {
    uint8_t c = pgm_read_byte(p++);
    write(c);
    crc = crc16Add(c, crc);
  }

  // output padding
  uint16_t pad = (4-(len&3))&3;
  uint8_t temp = 0;
  while (pad--) {
    write(temp);
    crc = crc16Add(temp, crc);
  }
}
*/

// Append the final CRC to the request and finish the request
void STMClient::Request(void) {
  write((uint8_t*)&crc, 2);
  
  _serial->putc(SLIP_END);
}

//===== Initialization

void STMClient::init() {
  _proto.buf = _protoBuf;
  _proto.bufSize = sizeof(_protoBuf);
  _proto.dataLen = 0;
  _proto.isEsc = 0;
}

STMClient::STMClient(Serial* serial) :
_serial(serial) {
  _debugEn = false;
  init();
}

STMClient::STMClient(Serial* serial, Serial* debug) :
_debug(debug), _serial(serial) {
  _debugEn = true;
  init();
}

void STMClient::DBG(const char* info) {
  if (_debugEn) _debug->printf(info);
}

//===== Responses

// Wait for a response for a given timeout
STMClientPacket *STMClient::WaitReturn(uint32_t timeout) {
  uint32_t wait = millis();
  while (millis() - wait < timeout) {
    STMClientPacket *packet = Process();
    if (packet != NULL) return packet;
  }
  return NULL;
}

//===== CRC hSTMper functions

uint16_t STMClient::crc16Add(unsigned char b, uint16_t acc)
{
  acc ^= b;
  acc = (acc >> 8) | (acc << 8);
  acc ^= (acc & 0xff00) << 4;
  acc ^= (acc >> 8) >> 4;
  acc ^= (acc & 0xff00) >> 5;
  return acc;
}

uint16_t STMClient::crc16Data(const unsigned char *data, uint16_t len, uint16_t acc)
{
  for (uint16_t i=0; i<len; i++)
    acc = crc16Add(*data++, acc);
  return acc;
}

//===== Basic requests built into STMClient

bool STMClient::Sync(uint32_t timeout) {
  //_debug->printf("syncing...");
  wait(0.5);
  // send sync request
  Request(CMD_SYNC, (uint32_t)&wifiCb, 0);
  Request();
  
  // empty the response queue hoping to find the wifiCb address
  STMClientPacket *packet;
  while ((packet = WaitReturn(timeout)) != NULL) {
    if (packet->value == (uint32_t)&wifiCb) 
    { 
    //    _debug->printf("SYNC!");  
        wait(0.5); 
        return true; 
    }
    _debug->printf("BAD: %s /n/r", packet->value);
  }
  
  // doesn't look like we got a real response
  return false;
}
 //look in cmd.c for this. Puts ESP to sleep until a fall edge triggers on RST for it to reset.
 
void STMClient::Sleep(void){
  Request(CMD_SLEEP, 0, 0);
  Request();
}

void STMClient::GetWifiStatus(void) {
  Request(CMD_WIFI_STATUS, 0, 0);
  Request();
}