I'm starting a project that has 2 serial devices connected to Teensy 3.2. I'm looking to see if I'm on the right track on setting everything up. This will expand to another serial uart and a CAN device, along with SPI port.
Serial 1 - Host
Format - HEX
RX - Gets load of information in hex with frameflags, CRC, etc
TX - Read data from RX, subtract the TTL (Time to Live) field and send back out
I had to create my own buffer (head & tail) to handle the flow, even though the serial has it's own buffer in the 'print' libraries. Not sure if I had to do that, or if there is a better way?
Serial 2 - Wifi
Format - Human readable
TX - Takes certain data fields from Serial 1, and sends out human readable to ESPwifi to Android tablets.
RX - Gets the odd user command back from tablet
As my project got more complicated, these simple "print" commands started to block S1 from receiving complete transmissions and started to get more CRC errors.
I attached the small zip file of the project. Also here is the code.
Serial_V1
S1
S3
Serial 1 - Host
Format - HEX
RX - Gets load of information in hex with frameflags, CRC, etc
TX - Read data from RX, subtract the TTL (Time to Live) field and send back out
I had to create my own buffer (head & tail) to handle the flow, even though the serial has it's own buffer in the 'print' libraries. Not sure if I had to do that, or if there is a better way?
Serial 2 - Wifi
Format - Human readable
TX - Takes certain data fields from Serial 1, and sends out human readable to ESPwifi to Android tablets.
RX - Gets the odd user command back from tablet
As my project got more complicated, these simple "print" commands started to block S1 from receiving complete transmissions and started to get more CRC errors.
I attached the small zip file of the project. Also here is the code.
Serial_V1
Code:
#include <Metro.h> //Timer
#include <TimeLib.h>
#define S1 Serial1
#define S3 Serial3
boolean S1Stat, S3Stat; //Connected?
uint16_t S1Cnt, S3Cnt; //Transmission counts
uint16_t Rx1_CrcError, Rx1_CrcValid; //Track valid & invalid frames
/* S1 */
void S1_Setup();
void S1_Check();
/* S3 */
void S3_Setup();
void S3_Check();
void setup() {
Serial.begin(57600);
S1_Setup();
S3_Setup();
}
void loop() {
//Check GRT from interlink
S1_Check();
//Check WIFI from android
S3_Check();
}
S1
Code:
#include <FastCRC.h>
#include <FastCRC_cpu.h>
#include <FastCRC_tables.h>
Metro S1Chk_TMR = Metro(10000); // Check connections every 10 second
Metro Hello_TMR = Metro(10000); // Send Hello packet to GRT every (10 sec)
// set this to the hardware serial port you wish to use
FastCRC16 CRC16; //CRC16_X25 http://www.sunshine2k.de/coding/javascript/crc/crc_js.html
const uint16_t SERIALBUFF = 512;
const uint8_t FRAMEFLAG = 0x7E, //Frame Flag
VENDORCODE = 0x5B, //Vendor protocol code
SOURCE = 0x05, //SourceID is 5
DEST = 0xFF, //Broadcast to all devices (255)
TTL = 0x0A; //Time to Live (number of hops between devices)
void Tx1_Prep(uint8_t msg[], uint16_t len);
void Tx1_Store(uint8_t val, uint8_t chrstuff);
void Tx1_Send();
void Rx1_Process(uint8_t data[], uint16_t len);
uint16_t Rx1Len, Tx1Head, Tx1Tail, Tx1Cap=SERIALBUFF, Tx1Len, Tx1Time, Rx1Time;
uint8_t Rx1Msg[SERIALBUFF], Rx1PrevByte, Tx1Msg[SERIALBUFF], Tx1Packet, Tx1Payload[100];
boolean Rx1Header=false, Tx1Sending=false;
boolean newData = false;
void S1_Setup() {
S1.begin(19200);
}
void S1_Check() {
// If the timer is reached, S1 is not connected
if (S1Chk_TMR.check()) {
S1Stat = false;
}
//Send Hello packet to S1
if (Hello_TMR.check()) {
S1_Hello();
}
/* Check if there is anything to TX in array */
if (Tx1Head != Tx1Tail) {
if (Tx1Sending == false) {
Tx1Sending = true;
}
Tx1_Send();
}
else {
if (Tx1Sending == true) {
Tx1Sending = false;
}
}
/* Check incoming serial and store to array one byte at a time */
static boolean recvInProgress = false;
static boolean stuffedByte = false;
static uint16_t ndx = 0;
uint8_t rb;
while (S1.available() > 0 && newData == false) {
rb = S1.read();
if (recvInProgress == true) {
if (rb != FRAMEFLAG) { //Start and End frame flag
//Character stuffing
if (stuffedByte == true) { //previous byte was stuffed
stuffedByte = false;
if (rb == 0x5E) { //7D5E = 7E
Rx1Msg[ndx-1] = 0x7E;
}
else if (rb == 0x5D) { //7D5D = 7D
Rx1Msg[ndx-1] = 0x7D;
}
//stop here and don't do anything else now. go to next char
}
else {
if (rb == 0x7D) {stuffedByte = true;} //Store stuffed byte for next byte compare
Rx1Msg[ndx] = rb;
ndx++;
if (ndx >= SERIALBUFF) {
ndx = SERIALBUFF - 1;
}
}
}
else {
if (ndx == 0) { //grabbed end frame first, now we are at the the start flag
recvInProgress = true;
}
else {
// Rx1Msg[ndx] = '\0'; //terminte the string
recvInProgress = false;
newData = true;
Rx1Len = ndx;
ndx = 0;
}
}
}
else if (rb == FRAMEFLAG) {
recvInProgress = true;
}
}
//There is new data, test the CRC now
if (newData == true) {
newData = false;
if (CRC16.x25(Rx1Msg, Rx1Len-2) == (uint16_t)((Rx1Msg[Rx1Len-1] << 8) | Rx1Msg[Rx1Len-2])) {
Rx1_CrcValid++;
Rx1_Process(Rx1Msg, Rx1Len-2); //Subtract the checksum bytes since we don't need to calculate it anymore
}
else {
Rx1_CrcError++; //Crc do not match
}
}
}
void S1_Hello() {
Tx1Packet = 0x00; //Hello
Tx1Len = 3;
memset(Tx1Payload,0,Tx1Len);
Tx1Payload[0] = 0x01; //Interlink version
Tx1Payload[1] = 0x00; //MSB of serial number
Tx1Payload[2] = 0x01; //LSB of serial number
Tx1_Payload(Tx1Packet, Tx1Payload, Tx1Len);
}
void Tx1_Payload(uint8_t packet, uint8_t payload[], uint16_t len) {
uint8_t msg[500];
msg[0] = VENDORCODE;
msg[1] = SOURCE;
msg[2] = DEST;
msg[3] = TTL;
msg[4] = packet;
for (uint16_t i = 0; i < len; i++) {
msg[i+5] = payload[i];
}
Tx1_Prep(msg, len+5);
}
/* Add the Frameflags and checksum to store in Tx1Buffer */
void Tx1_Prep(uint8_t msg[], uint16_t len) {
uint16_t crc = CRC16.x25(msg, len);
Tx1_Store(FRAMEFLAG, 0); //Start frame
for (uint16_t i = 0; i < len; i++) {Tx1_Store(msg[i], 1);} //Store msg
Tx1_Store(lowByte(crc), 1);
Tx1_Store(highByte(crc), 1);
Tx1_Store(FRAMEFLAG, 0); //End frame
}
/* Store into Tx1Buffer array and increase Tx1Tail */
void Tx1_Store(uint8_t val, uint8_t chrstuff) {
if (Tx1Tail == (Tx1Head-1 + Tx1Cap) % Tx1Cap) {Serial.println("** Tx1 Buffer full **");}
// character stuffing
if (chrstuff == 1 && val == 0x7E) //7E = 7D5E
{ Tx1Msg[Tx1Tail] = 0x7D;
Tx1Tail = (Tx1Tail + 1) % Tx1Cap;
val = 0x5E;
}
if (chrstuff == 1 && val == 0x7D) //7D = 7D5D
{ Tx1Msg[Tx1Tail] = 0x7D;
Tx1Tail = (Tx1Tail + 1) % Tx1Cap;
val = 0x5D;
}
Tx1Msg[Tx1Tail] = val;
Tx1Tail = (Tx1Tail + 1) % Tx1Cap;
}
/* Send what is in TX array to SerialPort */
void Tx1_Send() {
uint16_t avl = S1.availableForWrite();
uint16_t remain;
if (Tx1Head > Tx1Tail) //looped over back to start of buffer
{ remain = (Tx1Tail + Tx1Cap) - Tx1Head; }
else
{ remain = Tx1Tail - Tx1Head; }
if (avl > 0)
{
if (avl > remain) {avl = remain;} //only process the lower of 1)what's remaining, 2)available for write
for (uint16_t i=0; i < avl; i++)
{
S1.write(Tx1Msg[Tx1Head]);
Tx1Head = (Tx1Head + 1) % Tx1Cap; //Roll over when past the buffer capacity
}
}
}
/* Process data packets */
void Rx1_Process(uint8_t data[], uint16_t len){
uint8_t msg[SERIALBUFF];
memcpy(msg, data, len+2);
uint8_t scr, ttl, packet;
// vendorcode = msg[0];
scr = msg[1];
// dest = msg[2];
ttl = msg[3];
packet = msg[4];
if (scr == SOURCE) {return;} //Packet has bounced back to you, ignore
if (packet == 0x00) { //Hello packet
S1Chk_TMR.reset();
S1Stat = true;
}
//Pass along message, -1 from Time to Live
ttl--;
msg[3] = ttl;
if (ttl == 0x00)
{ Serial.print("Packet TTL is dead");
return;
}
Tx1_Prep(msg, len); //repeat serial packet for next device in loop
}
S3
Code:
Metro S3Chk_TMR = Metro(10000); // Check connections every 10 second
Metro S3Data_TMR = Metro(500); // Send Data every 0.5 second
int readline(int readch, char *buffer, int len);
const uint16_t SERIAL3BUFF = 512;
uint16_t Rx3Len, Tx3Head, Tx3Tail, Tx3Cap=SERIAL3BUFF, Tx3Len, Tx3Time, Rx3Time;
uint8_t Rx3Msg[SERIAL3BUFF], Rx3PrevByte, Tx3Msg[SERIAL3BUFF], Tx3Packet, Tx3Payload[100];
boolean Rx3Header=false, Tx3Sending=false;
boolean newData3 = false;
void S3_Setup() {
S3.begin(57600);
S3Cnt = 0;
S1Cnt = 0;
}
void S3_Check() {
//If timer hits here, then the BCM is not connected
if (S3Chk_TMR.check()) {
S3Stat = false;
}
if (S3Data_TMR.check() ) {
S3Cnt = S3Cnt + 1; //65536
String data = "*S3"
+String(",")+ S3Cnt
+String(",")+ S1Stat //Is S1 Connected? 0 - No, 1-Yes
+String(",")+ S3Stat; //Is S3 Connected? 0 - No, 1-Yes
S3.println(data);
if (S1Stat) {
S1Cnt = S1Cnt + 1; //65536
S3.println("*S1"
+String(",")+ S1Cnt
+String(",")+ Rx1_CrcValid
+String(",")+ Rx1_CrcError
);
}
}
if (S3.available() > 0) {
static char buffer[255];
if (readline(S3.read(), buffer, 255) > 0) {
String msg = String(buffer);
if (msg.substring(0,4) == "BCM:") {
if (msg.substring(4) == "HELLO") { //Receives 'Hello' to see if tablet is connected
S3Chk_TMR.reset();
S3Stat = true;
}
}
}
}
}
//ASCII sent over serial, look for newline (CR) for end of tranmission
int readline(int readch, char *buffer, int len)
{
static int pos = 0;
int rpos;
if (readch > 0) {
switch (readch) {
case '\n': // Ignore new-lines
break;
case '\r': // Return on CR
rpos = pos;
pos = 0; // Reset position index ready for next time
return rpos;
default:
if (pos < len-1) {
buffer[pos++] = readch;
buffer[pos] = 0;
}
}
}
// No end of line has been found, so return -1.
return -1;
}