CANBUS - FlexCAN

Hi A very short intro at lecture room and then

“Selfstudy/workshop”. You can call me by email. I will be available 15 min after intro lecture

This page is organised as

  1. FlexCAN and Teensy - you will borrow a Teensy at lecture. I have in total 5 so we might have to do some rotation

  2. Ordering of events and clocks in a distributed system

    1. read it and view the presentations

  3. and in the bottom of this page some helpfull CAN test programs. RTo be used the following lectures

Monday will be used for

  1. getting you all up and ready for network calculus

    1. by getting your Teensy 3.6 up and running

    2. link1

    3. link 2

    4. install FlexCan library for CANBUS

    5. see here for code

NBNBNB: You can find many FlexCAN implementations out in “the cloud” The one above has been tested by Henrik and me and seems to work …

Exercises for CANBUS part

Inspect code in

a) In the write call (starting from line 223)

question; what is the difference in the outgoing part with timeout == 0 and timeout != 0 (and positive).

b) in the read call (starting from line 170) question: how many rx buffers do we have ?

c) write a test program for buffers you can run the code on one teensy if you connect skeleton

for (int i=0; i < 10; i++)
{
    for (int a=0; a <=i; a++)
       "write_a_canmsg - let databyte no 0 have values 0,1,2 acc to loop with "i"

    read as many you can
}
  • do you miss messages ? (you can spot it by rcv of messages with byte = = 0,1,2,3,…

  • if yes does this say some about the buffering system in the rx part

  • if you dont loose messages whatif you set the “i” loop up to 20

What impact will it have to set timeout to 0 or 1 in the can pkg you use to receive data in when calling FlexCAN read (in line 170)

some CANBUS stuff

See

SELF STUDY PART

Clocks

Todays selfstudy is about clocks and ordering.

Basic problem is ordering of events on different machines.

  • is this always possible

  • what is the constraints

  • time and clocks

  • events and clocks

Today we can argue for that in many systems we can base and evaluate event ordering on time from satellites.

In some cases this may not be possible. So …

See a blog story which covers most of the stuff in a nice way.

Primary Litterature

Exercises

  • Draw and discuss the Clock figures from the slides

  • more on way (just coding a skeleton program for the Teensys)

Secondary litterature - is you have time

Some extra litterature

This as just things I have found during the years. Dont read it for today m but might be nice to have :-)

All litterature just above as a zip file

CANBUS code for FlexCAN

CANbus Exercise

  • Get this up and running

    • CAN bus is set to 50 k bit/Sec

    • a CAN package is approx 100 bit long so TX takes approx 2 milliseconds

    • teensy 3.6 with two caninterfaces

  • get an extra teensy (from group next door)

    • comment out tx master (which only do time packet TX)

  • Observe how time is adjusted

  • Add a third node and let it send a package every 3 millisecond with msg ID 23;

  • Observe

    • do you see larger time adjust ?

file canmastertime.ino


// --------------------------------------------------------------------
// DUAL Can program for teen sy 3.6CANtest for Teensy 3.6 dual CAN bus
// beerlicense  Jens Dalsgaard Nielsen Oct 2017
// inspired by Pawelsky's dualcantest
//
// --------------------------------------------------------------------


#include <FlexCAN.h>

#ifndef __MK66FX1M0__
#error "Teensy 3.6 with dual CAN bus is required to run this example"
#endif

FlexCAN CANbus0(50000, 0, 1, 1);  // 1 Mbit CAN0, 1,1 routes CAN0 to pins 29 & 30
FlexCAN CANbus1(50000, 1, 0, 0);  // 1 Mbit CAN1  0,0 has no function - is always on pins 33 & 34

static CAN_message_t msgMasterBuf, msgSlaveBuf;   // buffers for master and slave


//#define MASTER

// If MASTER is not defined (just above ) we define SLAVE
#ifndef MASTER

#define SLAVE

#endif


/*----------------------------------------------------------------
   typedef struct CAN_message_t {
   uint32_t id; // can identifier
   uint8_t ext; // identifier is extended
   uint8_t len; // length of data
   uint16_t timeout; // milliseconds, zero will disable waiting
   uint8_t buf[8];
   } CAN_message_t;
  ----------------------------------------------------------------*/

void wrInt(uint8_t buf[], unsigned long i , int pos)
{
  unsigned long ii;
  ii = i;
  memcpy(&buf[pos], &(ii), sizeof(unsigned long)); // copy int to array on position "pos"
}

unsigned long rdInt(uint8_t buf[], int pos)
{
  unsigned long i;
  memcpy(&i, &(buf[pos]), sizeof(unsigned long)); // copy int to array on position "pos"
  return i;
}


void dataDump(uint8_t data[])  // just print 8 bytes data
{
  int i;
  Serial.print("CAN data: ");

  for (i = 0; i < 8 ; i++) {
    Serial.print(data[i]);
  }

  Serial.println("");
}


void CANPackageDump(CAN_message_t pk)   // prints CAN package
{

  // We just dump a CAN bus package on Serial
  //
  Serial.print("CAN package dump. objektID: ");
  Serial.print(pk.id);   // objektID in package

  if (pk.ext == 1) {
    Serial.print(" ext objektID (29 bit) ");
  }
  else {
    Serial.print(" short objektID(11bit) ");
  }

  Serial.print(" ");

  dataDump(pk.buf);      // dump 8 byte data
}



void initMsg(CAN_message_t *pMessage, int objektID)
{
  pMessage->id = objektID;
  pMessage->ext = 0;         // 11 bit objektID

  pMessage->len = 8;         // all 8 byte data shall be transmitted

  for (int i = 0; i < 8; i++)
  {
    pMessage->buf[i] = i;    // write 0,1,2,...,7 in 8 byte in buf
  }
}

// -------------------------------------------------------------
// -------------------------------------------------------------
// -------------TRANSMIT AND RECEIVE FOR MASTER/SLAVE-----------
// -------------------------------------------------------------
// -------------------------------------------------------------


// -------------------------------------------------------------
// -------------------------------------------------------------
// ------------USAGE OF the 8 byte data in buf       -----------
// -------------------------------------------------------------
// -------------------------------------------------------------

/*
   Layout and use of the 8 data bytes in the CAN bus packet
   buf[0] : id of sender - legal interval 0-255
   buf[1]:  command (like 'R', 'W', 'C', 'T' etc
   buf[2]:  no of port for
   buf[4]..buf[7]: 4 bytes used for transport of time (unsigned long)
  R: read
  W: write
  C: config (port)
  T: global time
*/


/*
   master sends a time packet every 3 second
   the time is pick up from millis plus an offset on 100.000.000
   The offset is a dirty trick to be sure that two nodes can find each other
   It only require they are powered up with a max time distance of 100.000.000 milliseconds
   millis overflow at 2.000.000.000 So after 20 days the algoritms break as it is now
*/

const int masterID = 100;
const int slaveID = 101;
const int broadCastID = 50;
const int masterClockID = 05; // high pririty for clock use

const int timeBetweenTimePackets = 5000; // milli sec

int lastMasterTxTime = 0;

int masterTXCount = 0;



const unsigned long timeOff = 10000000;
unsigned long millisOffset = timeOff;



// our own millis timer syncronized function

unsigned long myMillis()
{
  return millis() + millisOffset;
}

int noClockAdjustUp = 0, noClockAdjustDown = 0;

void setMyClock(unsigned long timeFromClockMaster)
{
  unsigned long timeNow;
  unsigned long adjustT;
  timeNow = myMillis();

  if ( timeNow < timeFromClockMaster) {

    // locally we are behind masterclock
    adjustT = (timeFromClockMaster - timeNow);
    millisOffset += adjustT;
    noClockAdjustUp++;
    Serial.print("    offset(behind) adjust time no "); Serial.print(noClockAdjustUp);
    Serial.print(" adj amount "); Serial.print(adjustT); Serial.print(" new offset "); Serial.println(millisOffset);
  }
  else if (timeFromClockMaster < timeNow ) {
    // or we are ahead.
    adjustT = (timeNow - timeFromClockMaster);
    millisOffset -= adjustT;
    Serial.print("    offset(ahead) adjust -time no "); Serial.print(noClockAdjustDown);
    Serial.print(" adj amount "); Serial.print(adjustT); Serial.print(" new offset "); Serial.println(millisOffset);
    noClockAdjustDown++;
    // HMMMM what do you think about we can get same time again .. (back to the future syndrome)
  }
  else {
    Serial.println("    no clock adjust");
  }
}


void txMaster()
{
  unsigned long test;
  // if we are MASTER include the collowing code, othwerwises the txMAster function will just be empty
#ifdef MASTER
  if ( timeBetweenTimePackets < (myMillis() - lastMasterTxTime ) ) { // has one second passed since last Tx ?
    lastMasterTxTime = myMillis();                // for next second

    // we add 1000000 bq we assume that from start there is less than 100000000 msec (100000 seconds) be startupTime of two nodes

    wrInt(msgMasterBuf.buf, (lastMasterTxTime), 4); // copy lastMasterTxTime to can
    //package buf array positions 4,5,6,7 (in the end)
    test = rdInt(msgMasterBuf.buf, 4);

    msgMasterBuf.id = masterClockID; // give it high prority (== low number)
    msgMasterBuf.ext = 0;
    msgMasterBuf.buf[0] = 'T';  // time package so everybody can recognize it

    CANbus0.write(msgMasterBuf);

    Serial.print("TX-MS int from master tx buf ");
    Serial.print(test);
    Serial.print("    org                      ");
    Serial.println(lastMasterTxTime);

    masterTXCount++;  // now we have send one more

    Serial.print("TX master clock: ");
    Serial.println(myMillis());
  }
#endif
}

// receive part for master
void rxMaster()
{
  unsigned long timeFromClock;

  if ( CANbus0.available() ) {  // any messages arrived ?
    Serial.println("RX-MS");
    CANbus0.read(msgMasterBuf);

    // is it for me ?
    if (msgMasterBuf.id == masterID) {

      Serial.println("    master received a for-me package");

      switch (msgMasterBuf.buf[1]) {
        case 'R':
          break;
        case 'W':
          break;
        case 'C':
          break;
        default :;
      }
    }
    else if (msgMasterBuf.id == masterClockID) { // Is it a time sync package from clock master ?

      // if we are not master we should be able to do clock stuff
      // but for now we wil llet it be handled by the slave
      // so time adjustment code is commented out here
#ifndef MASTER
      // YES - double check it. There shall be a 'T' in buf[0];
      if (msgMasterBuf.buf[0] == 'T') { // time sync package

        timeFromClock = rdInt(msgMasterBuf.buf, 4); // lets get int which is in buf[4] .. buf[7] (int is size 4 bytes)

        //  no time adjustjment for now
        // setMyClock(timeFromClock);  // and set/test my local clock
        Serial.print("    RX time pkg with time: ");
        Serial.println(timeFromClock);

      }
#endif
    }
    else {
      Serial.print("    received a not-for-me packet with ID: ");
      Serial.println(msgMasterBuf.id);
    }
  }
}

// receive and reply for slave
int slaveTXCount = 0;
void rxSlave()
{
  unsigned long timeFromClock;==

  if ( CANbus1.available() ) {  // any messages arrived ?

    CANbus1.read(msgSlaveBuf);

    Serial.println("RX-SL");

    // is it for us ?
    if (msgSlaveBuf.id == slaveID) {

      Serial.println("RX+ slave received a for-me package");
      switch (msgSlaveBuf.buf[1]) {
        case 'R':
          msgSlaveBuf.id = masterID;
          msgSlaveBuf.ext = 0;  // 11 bit objektID
          // here you can put info in package to master
          CANbus1.write(msgSlaveBuf);
          slaveTXCount++;
          Serial.print("    TX slave: ");
          Serial.println(slaveTXCount);
          break;
        case 'W':
          break;
        case 'C':
          break;
        default :;
      }
    }
    // HERE WE DO CLOCK SYNC STUFF
    else if (msgSlaveBuf.id ==  masterClockID) {
#ifndef MASTER
      if (msgSlaveBuf.buf[0] == 'T') { // time sync package just an extra check

        timeFromClock = rdInt(msgSlaveBuf.buf, 4); // lets get unsigned long (32 bit) which is in buf[4] .. buf[7]

        Serial.print("    RX time pkg with time:          ");
        Serial.println(timeFromClock);
        Serial.print("    local time is just now(ahead ?) ");
        Serial.println( myMillis());

        // I know it takes time to print - but it will make problem more visible - time
        setMyClock(timeFromClock);  // and set/test my local clock

        // But this should be done with the slave on another node.
        // we cant send time msg from myself to me.
        // In this case transport delay will naturally every sync time give an adjustment
      }
#endif
    }
  }
}


void initAndStartCan0AndCan1()
{
  pinMode(28, OUTPUT);   // add on driver board for CAN0 enable pin (active LOW)
  pinMode(35, OUTPUT);  // add on driver board for CAN1 enable pin (active LOW)
  digitalWrite(28, LOW); // activate CAN bus transceiver/driver for can0
  digitalWrite(35, LOW); // activate 230 canbus transceiver for can1
  CANbus0.begin();      // intitalise and start CAN0 interface (to be used for master)
  CANbus1.begin();      // ... - slave interface
}
// -------------------------------------------------------------
// -------------------------------------------------------------
// ----------------------HERE WE START--------------------------
// -------------------------------------------------------------
// -------------------------------------------------------------

void setup(void)
{
  pinMode(13, OUTPUT);  // our famous LED on pin 13
  Serial.begin(115200);
  delay(1000);  // let USb serial get time to be activated
  Serial.println( sizeof(int));
  Serial.print(" Ul "); Serial.println(sizeof(unsigned long));

  Serial.println("Teensy 3.6 dual CAN bus timer test ");

  initMsg(&msgMasterBuf, 100); // 100 is objektID which will be written in CAN package
  initMsg(&msgSlaveBuf, 200); // 200 is ...

  initAndStartCan0AndCan1();

  Serial.println("gogo");
}


// -------------------------------------------------------------
void loop(void)
{

  // service as fast as possible - so NO delay in the functions !!! please :-)
  txMaster();
  rxMaster();
  rxSlave();
}

Another smart program

  1. Master sends a timesync every 5 second

  2. Slave adjust watch acc to drift

  3. Do also work with slave and master on same node because

    1. there is a seperate clock for time sync and thes rest (both based on millis)

name of code dualclock01


// --------------------------------------------------------------------
// DUAL Can program for teen sy 3.6CANtest for Teensy 3.6 dual CAN bus
// beerlicense  Jens Dalsgaard Nielsen Oct 2017
// inspired by Pawelsky's dualcantest
//
// --------------------------------------------------------------------


#include <FlexCAN.h>

#ifndef __MK66FX1M0__
#error "Teensy 3.6 with dual CAN bus is required to run this example"
#endif


/*----------------------------------------------------------------
   BASIC struct to send and to receive CAN bus messages
   typedef struct CAN_message_t {
   uint32_t id; // can identifier
   uint8_t ext; // identifier is extended
   uint8_t len; // length of data
   uint16_t timeout; // milliseconds, zero will disable waiting
   uint8_t buf[8];
   } CAN_message_t;
  ----------------------------------------------------------------*/

CAN_message_t msgMasterBuf, msgSlaveBuf;   // buffers for master and slave

FlexCAN CANbus0(50000, 0, 1, 1);  // 1 Mbit CAN0, 1,1 routes CAN0 to pins 29 & 30
FlexCAN CANbus1(50000, 1, 0, 0);  // 1 Mbit CAN1  0,0 has no function - is always on pins 33 & 34

// --------- CONFIG PART -------------------

#define MASTERCLOCK

#define BUGPR(x) Serial.print(x)

// who is who and who am I ?

const int myMasterID = 100;
const int mySlaveID = 101;
const int broadCastID = 50;
const int masterClockID = 05; // high pririty for clock use

// -----------------oOo-----------------

void wrInt(uint8_t buf[], unsigned long i , int pos) // -- write int/long(32bit=4 byte) to an array
{
  unsigned long ii;
  ii = i;
  memcpy(&buf[pos], &(ii), sizeof(unsigned long)); // copy int to array on position "pos"
}

unsigned long rdInt(uint8_t buf[], int pos) // -- read int/long(32bit=4 byte) to an array
{
  unsigned long i;
  memcpy(&i, &(buf[pos]), sizeof(unsigned long)); // copy int to array on position "pos"
  return i;
}

void dataDump(CAN_message_t canPackage)  // just print 8 bytes data (used for CANbus data buf
{
  int i;

  for (i = 0; i < 8 ; i++) {
    BUGPR(canPackage.buf[i]);
    BUGPR(" ");
  }

}

void CANPackageDump(CAN_message_t pk)   // prints CAN package
{

  // We just dump a CAN bus package on Serial
  //

  if (pk.ext == 1) {
    BUGPR(" (29 bit) ");
  }
  else {
    BUGPR(" (11bit) ");
  }

  BUGPR("id length buf[8] ");
  BUGPR(pk.id);   // objektID in package
  BUGPR(" ");
  BUGPR(pk.len);
  BUGPR(" : ");

  dataDump(pk);      // dump 8 byte data
  BUGPR("\n");
}

// -------------------------------------------------------------
// -------------------------------------------------------------
// -------------TRANSMIT AND RECEIVE FOR MASTER/SLAVE-----------
// -------------------------------------------------------------
// -------------------------------------------------------------


// -------------------------------------------------------------
// -------------------------------------------------------------
// ------------USAGE OF the 8 byte data in buf       -----------
// -------------------------------------------------------------
// -------------------------------------------------------------

/*
   Layout and use of the 8 data bytes in the CAN bus packet
   buf[0] : id of sender - legal interval 0-255
   buf[1]:  command (like 'R', 'W', 'C', 'T' etc
   buf[2]:  no of port for
   buf[4]..buf[7]: 4 bytes used for transport of time (unsigned long)
  R: read
  W: write
  C: config (port)
  T: global time

   master sends a time packet every 3 second
   the time is pick up from millis plus an offset on 100.000.000
   The offset is a dirty trick to be sure that two nodes can find each other
   It only require they are powered up with a max time distance of 100.000.000 milliseconds
   millis overflow at 2.000.000.000 So after 20 days the algoritms break as it is now
*/

const int timeBetweenTimePackets = 5000; // milli sec

int lastMasterTxTime = 0;  // time for last time sync/watch adjustement packet sedn from TxMasterr
int masterTXCount = 0;     // and how many has been send

int noClockAdjustUp = 0, noClockAdjustDown = 0;  // how many tiems has slave adjusted watch being behind or ahead

const unsigned long timeOff = 10000000;   // Our startTime is 0 + ...
unsigned long millisOffset = 0;           // time adjust part for slave

// our own millis timer syncronized function for everybody except TxMaster where it sneds tiem sync packages

//------------------------------------------------------oOo---------------------------------------
unsigned long myMillis()
{
  return millis() + millisOffset; // millisOffset will change when watch is syncronized
}

//------------------------------------------------------oOo---------------------------------------
unsigned long masterClockMillis()
{
  return millis() + timeOff;  // timeOff is constant
}

//------------------------------------------------------oOo---------------------------------------
void setMyClock(unsigned long timeFromClockMaster)
{
  unsigned long timeNow;
  unsigned long adjustT;
  timeNow = myMillis();

  if ( timeNow < timeFromClockMaster) {   // locally we are behind masterclock

    adjustT = (timeFromClockMaster - timeNow);
    millisOffset += adjustT;
    noClockAdjustUp++;

    BUGPR("    offset(behind) adjust time no "); BUGPR(noClockAdjustUp);
    BUGPR(" adj amount "); BUGPR(adjustT); BUGPR(" new offset "); BUGPR(millisOffset);
    BUGPR("\n");
  }
  else if (timeFromClockMaster < timeNow ) {      // or we are ahead.
    adjustT = (timeNow - timeFromClockMaster);
    millisOffset -= adjustT;
    noClockAdjustDown++;

    BUGPR("    offset(ahead) adjust -time no "); BUGPR(noClockAdjustDown);
    BUGPR(" adj amount "); BUGPR(adjustT); BUGPR(" new offset "); BUGPR(millisOffset);
    BUGPR("\n");
    // HMMMM what do you think about we can get same time again .. (back to the future syndrome)
  }
  else {
    BUGPR("    no clock adjust this time\n");
  }
}

//------------------------------------------------------oOo---------------------------------------
void tstAndSendTxClockSyncPackage()
{
#ifdef MASTERCLOCK
  unsigned long timeNow;

  timeNow = masterClockMillis();

  if ( timeBetweenTimePackets < (timeNow - lastMasterTxTime ) ) { // has one second passed since last Tx ?

    lastMasterTxTime = timeNow;                // for next second

    wrInt(msgMasterBuf.buf, timeNow, 4); // copy lastMasterTxTime to can
    //package buf array positions 4,5,6,7 (in the end)

    msgMasterBuf.id = masterClockID; // give it high prority (== low number)
    msgMasterBuf.ext = 0;  // 11 bit address
    msgMasterBuf.buf[0] = 'T';  // time package so everybody can recognize it
    msgMasterBuf.len = 8;

    CANbus1.write(msgMasterBuf);

    BUGPR("TX-MS int from master tx buf no");
    BUGPR(masterTXCount);
    BUGPR(" time now is: ");
    BUGPR(timeNow);
    BUGPR("\n");
    masterTXCount++;  // now we have send one more
  }
#endif

}

//------------------------------------------------------oOo---------------------------------------
void txMaster()
{
  // if we are MASTER include the collowing code, othwerwises the txMAster function will just be empty
#ifdef MASTERCLOCK

  tstAndSendTxClockSyncPackage();

#endif
}

//------------------------------------------------------oOo---------------------------------------
void rxMasterPersonalPackage(CAN_message_t CANpk)
{
  BUGPR("    master received a for-me package\n");

  switch (CANpk.buf[1]) {
    case 'R':
      break;
    case 'W':
      break;
    case 'C':
      break;
// --------------------------------------------------------------------
// DUAL Can program for teen sy 3.6CANtest for Teensy 3.6 dual CAN bus
// beerlicense  Jens Dalsgaard Nielsen Oct 2017
// inspired by Pawelsky's dualcantest
//
// --------------------------------------------------------------------


#include <FlexCAN.h>

#ifndef __MK66FX1M0__
#error "Teensy 3.6 with dual CAN bus is required to run this example"
#endif


/*----------------------------------------------------------------
   BASIC struct to send and to receive CAN bus messages
   typedef struct CAN_message_t {
   uint32_t id; // can identifier
   uint8_t ext; // identifier is extended
   uint8_t len; // length of data
   uint16_t timeout; // milliseconds, zero will disable waiting
   uint8_t buf[8];
   } CAN_message_t;
  ----------------------------------------------------------------*/

CAN_message_t msgMasterBuf, msgSlaveBuf;   // buffers for master and slave

FlexCAN CANbus0(50000, 0, 1, 1);  // 1 Mbit CAN0, 1,1 routes CAN0 to pins 29 & 30
FlexCAN CANbus1(50000, 1, 0, 0);  // 1 Mbit CAN1  0,0 has no function - is always on pins 33 & 34

// --------- CONFIG PART -------------------

#define MASTERCLOCK

#define BUGPR(x) Serial.print(x)

// who is who and who am I ?

const int myMasterID = 100;
const int mySlaveID = 101;
const int broadCastID = 50;
const int masterClockID = 05; // high pririty for clock use

// -----------------oOo-----------------

void wrInt(uint8_t buf[], unsigned long i , int pos) // -- write int/long(32bit=4 byte) to an array
{
  unsigned long ii;
  ii = i;
  memcpy(&buf[pos], &(ii), sizeof(unsigned long)); // copy int to array on position "pos"
}

unsigned long rdInt(uint8_t buf[], int pos) // -- read int/long(32bit=4 byte) to an array
{
  unsigned long i;
  memcpy(&i, &(buf[pos]), sizeof(unsigned long)); // copy int to array on position "pos"
  return i;
}

void dataDump(CAN_message_t canPackage)  // just print 8 bytes data (used for CANbus data buf
{
  int i;

  for (i = 0; i < 8 ; i++) {
    BUGPR(canPackage.buf[i]);
    BUGPR(" ");
  }

}

void CANPackageDump(CAN_message_t pk)   // prints CAN package
{

  // We just dump a CAN bus package on Serial
  //

  if (pk.ext == 1) {
    BUGPR(" (29 bit) ");
  }
  else {
    BUGPR(" (11bit) ");
  }

  BUGPR("id length buf[8] ");
  BUGPR(pk.id);   // objektID in package
  BUGPR(" ");
  BUGPR(pk.len);
  BUGPR(" : ");

  dataDump(pk);      // dump 8 byte data
  BUGPR("\n");
}

// -------------------------------------------------------------
// -------------------------------------------------------------
// -------------TRANSMIT AND RECEIVE FOR MASTER/SLAVE-----------
// -------------------------------------------------------------
// -------------------------------------------------------------


// -------------------------------------------------------------
// -------------------------------------------------------------
// ------------USAGE OF the 8 byte data in buf       -----------
// -------------------------------------------------------------
// -------------------------------------------------------------

/*
   Layout and use of the 8 data bytes in the CAN bus packet
   buf[0] : id of sender - legal interval 0-255
   buf[1]:  command (like 'R', 'W', 'C', 'T' etc
   buf[2]:  no of port for
   buf[4]..buf[7]: 4 bytes used for transport of time (unsigned long)
  R: read
  W: write
  C: config (port)
  T: global time

   master sends a time packet every 3 second
   the time is pick up from millis plus an offset on 100.000.000
   The offset is a dirty trick to be sure that two nodes can find each other
   It only require they are powered up with a max time distance of 100.000.000 milliseconds
   millis overflow at 2.000.000.000 So after 20 days the algoritms break as it is now
*/

const int timeBetweenTimePackets = 5000; // milli sec

int lastMasterTxTime = 0;  // time for last time sync/watch adjustement packet sedn from TxMasterr
int masterTXCount = 0;     // and how many has been send

int noClockAdjustUp = 0, noClockAdjustDown = 0;  // how many tiems has slave adjusted watch being behind or ahead

const unsigned long timeOff = 10000000;   // Our startTime is 0 + ...
unsigned long millisOffset = 0;           // time adjust part for slave

// our own millis timer syncronized function for everybody except TxMaster where it sneds tiem sync packages

//------------------------------------------------------oOo---------------------------------------
unsigned long myMillis()
{
  return millis() + millisOffset; // millisOffset will change when watch is syncronized
}

//------------------------------------------------------oOo---------------------------------------
unsigned long masterClockMillis()
{
  return millis() + timeOff;  // timeOff is constant
}

//------------------------------------------------------oOo---------------------------------------
void setMyClock(unsigned long timeFromClockMaster)
{
  unsigned long timeNow;
  unsigned long adjustT;
  timeNow = myMillis();

  if ( timeNow < timeFromClockMaster) {   // locally we are behind masterclock

    adjustT = (timeFromClockMaster - timeNow);
    millisOffset += adjustT;
    noClockAdjustUp++;

    BUGPR("    offset(behind) adjust time no "); BUGPR(noClockAdjustUp);
    BUGPR(" adj amount "); BUGPR(adjustT); BUGPR(" new offset "); BUGPR(millisOffset);
    BUGPR("\n");
  }
  else if (timeFromClockMaster < timeNow ) {      // or we are ahead.
    adjustT = (timeNow - timeFromClockMaster);
    millisOffset -= adjustT;
    noClockAdjustDown++;

    BUGPR("    offset(ahead) adjust -time no "); BUGPR(noClockAdjustDown);
    BUGPR(" adj amount "); BUGPR(adjustT); BUGPR(" new offset "); BUGPR(millisOffset);
    BUGPR("\n");
    // HMMMM what do you think about we can get same time again .. (back to the future syndrome)
  }
  else {
    BUGPR("    no clock adjust this time\n");
  }
}

//------------------------------------------------------oOo---------------------------------------
void tstAndSendTxClockSyncPackage()
{
#ifdef MASTERCLOCK
  unsigned long timeNow;

  timeNow = masterClockMillis();

  if ( timeBetweenTimePackets < (timeNow - lastMasterTxTime ) ) { // has one second passed since last Tx ?

    lastMasterTxTime = timeNow;                // for next second

    wrInt(msgMasterBuf.buf, timeNow, 4); // copy lastMasterTxTime to can
    //package buf array positions 4,5,6,7 (in the end)

    msgMasterBuf.id = masterClockID; // give it high prority (== low number)
    msgMasterBuf.ext = 0;  // 11 bit address
    msgMasterBuf.buf[0] = 'T';  // time package so everybody can recognize it
    msgMasterBuf.len = 8;

    CANbus1.write(msgMasterBuf);

    BUGPR("TX-MS int from master tx buf no");
    BUGPR(masterTXCount);
    BUGPR(" time now is: ");
    BUGPR(timeNow);
    BUGPR("\n");
    masterTXCount++;  // now we have send one more
  }
#endif

}

//------------------------------------------------------oOo---------------------------------------
void txMaster()
{
  // if we are MASTER include the collowing code, othwerwises the txMAster function will just be empty
#ifdef MASTERCLOCK

  tstAndSendTxClockSyncPackage();

#endif
}

//------------------------------------------------------oOo---------------------------------------
void rxMasterPersonalPackage(CAN_message_t CANpk)
{
  BUGPR("    master received a for-me package\n");

  switch (CANpk.buf[1]) {
    case 'R':
      break;
    case 'W':
      break;
    case 'C':
      break;
    default :;
  }
}

//------------------------------------------------------oOo---------------------------------------
void doClockSync(CAN_message_t CANpk)
{
  unsigned long timeFromClock;
  timeFromClock = rdInt(CANpk.buf, 4); // lets get int which is in buf[4] .. buf[7] (int is size 4 bytes)

  //  no time adjustjment for now
  setMyClock(timeFromClock);  // and set/test my local clock
  BUGPR("    RX time pkg with time: ");
  BUGPR(timeFromClock);
  BUGPR("\n");
}

// receive part for master
//------------------------------------------------------oOo---------------------------------------
void rxMaster()
{
  unsigned long timeFromClock;

  if ( CANbus1.available() ) {  // any messages arrived ?

    CANbus1.read(msgMasterBuf);

    BUGPR("RX-MS\n");

    if (msgMasterBuf.id == myMasterID) {     // is it for me personally ?

      rxMasterPersonalPackage(msgMasterBuf);

    }
    else if (msgMasterBuf.id == masterClockID) { // Is it a time sync package from clock master ?

      // if we are not master we should be able to do clock stuff
      // but for now we wil llet it be handled by the slave
      // so time adjustment code is commented out here

      // YES - double check it. There shall be a 'T' in buf[0];
      if (msgMasterBuf.buf[0] == 'T') { // time sync package

        doClockSync(msgMasterBuf);
      }
    }
    else {
      return;
      BUGPR("    received a not-for-me packet with ID: ");
      BUGPR(msgMasterBuf.id);
      BUGPR("\n");
    }
  }
}


void rxSlavePersonalPackage(CAN_message_t CANpk)
{
  BUGPR("    master received a for-me package");

  switch (CANpk.buf[1]) {
    case 'R':
      CANpk.id = myMasterID;
      CANpk.ext = 0;  // 11 bit objektID
      // here you can put info in package to master
      CANbus1.write(CANpk);
      BUGPR("    TX slave to ... ");
      BUGPR(CANpk.id);
      break;
    case 'W':
      break;
    case 'C':
      break;
    default :;
  }
  BUGPR("\n");
}


// receive and reply for slave
//------------------------------------------------------oOo---------------------------------------
void rxSlave()
{
  unsigned long timeFromClock;

  if ( CANbus0.available() ) {  // any messages arrived ?

    CANbus0.read(msgSlaveBuf);

    BUGPR("RX SL pak received ");

    CANPackageDump(msgSlaveBuf);

    if (msgSlaveBuf.id == mySlaveID) {     // is it for us ?

      BUGPR("RX+ slave received a for-me package\n");

      rxSlavePersonalPackage(msgSlaveBuf);
    }
    // HERE WE DO CLOCK SYNC STUFF
    else if (msgSlaveBuf.id ==  masterClockID) {

      if (msgSlaveBuf.buf[0] == 'T') { // time sync package just an extra check

        doClockSync(msgSlaveBuf);

      }
    }
  }
}


//------------------------------------------------------oOo---------------------------------------
void initAndStartCan0AndCan1()
{
  pinMode(28, OUTPUT);   // add on driver board for CAN0 enable pin (active LOW)
  pinMode(35, OUTPUT);  // add on driver board for CAN1 enable pin (active LOW)
  digitalWrite(28, LOW); // activate CAN bus transceiver/driver for can0
  digitalWrite(35, LOW); // activate 230 canbus transceiver for can1
  CANbus0.begin();      // intitalise and start CAN0 interface (to be used for master)
  CANbus1.begin();      // ... - slave interface
}
// -------------------------------------------------------------
// -------------------------------------------------------------
// ----------------------HERE WE START--------------------------
// -------------------------------------------------------------
// -------------------------------------------------------------

//------------------------------------------------------oOo---------------------------------------
void setup(void)
{
  pinMode(13, OUTPUT);  // our famous LED on pin 13
  Serial.begin(115200);
  delay(1000);  // let USb serial get time to be activated



  BUGPR("Teensy 3.6 dual CAN bus timer clock set test \n");

  initAndStartCan0AndCan1();

  BUGPR("gogo\n");
}


//------------------------------------------------------oOo---------------------------------------
void loop(void)
{
  // service as fast as possible - so NO delay in the functions !!! please :-)
  txMaster();
  rxMaster();
  rxSlave();
}


    default :;
  }
}

//------------------------------------------------------oOo---------------------------------------
void doClockSync(CAN_message_t CANpk)
{
  unsigned long timeFromClock;
  timeFromClock = rdInt(CANpk.buf, 4); // lets get int which is in buf[4] .. buf[7] (int is size 4 bytes)

  //  no time adjustjment for now
  setMyClock(timeFromClock);  // and set/test my local clock
  BUGPR("    RX time pkg with time: ");
  BUGPR(timeFromClock);
  BUGPR("\n");
}

// receive part for master
//------------------------------------------------------oOo---------------------------------------
void rxMaster()
{
  unsigned long timeFromClock;

  if ( CANbus1.available() ) {  // any messages arrived ?

    CANbus1.read(msgMasterBuf);

    BUGPR("RX-MS\n");

    if (msgMasterBuf.id == myMasterID) {     // is it for me personally ?

      rxMasterPersonalPackage(msgMasterBuf);

    }
    else if (msgMasterBuf.id == masterClockID) { // Is it a time sync package from clock master ?

      // if we are not master we should be able to do clock stuff
      // but for now we wil llet it be handled by the slave
      // so time adjustment code is commented out here

      // YES - double check it. There shall be a 'T' in buf[0];
      if (msgMasterBuf.buf[0] == 'T') { // time sync package

        doClockSync(msgMasterBuf);
      }
    }
    else {
      return;
      BUGPR("    received a not-for-me packet with ID: ");
      BUGPR(msgMasterBuf.id);
      BUGPR("\n");
    }
  }
}


void rxSlavePersonalPackage(CAN_message_t CANpk)
{
  BUGPR("    master received a for-me package");

  switch (CANpk.buf[1]) {
    case 'R':
      CANpk.id = myMasterID;
      CANpk.ext = 0;  // 11 bit objektID
      // here you can put info in package to master
      CANbus1.write(CANpk);
      BUGPR("    TX slave to ... ");
      BUGPR(CANpk.id);
      break;
    case 'W':
      break;
    case 'C':
      break;
    default :;
  }
  BUGPR("\n");
}


// receive and reply for slave
//------------------------------------------------------oOo---------------------------------------
void rxSlave()
{
  unsigned long timeFromClock;

  if ( CANbus0.available() ) {  // any messages arrived ?

    CANbus0.read(msgSlaveBuf);

    BUGPR("RX SL pak received ");

    CANPackageDump(msgSlaveBuf);

    if (msgSlaveBuf.id == mySlaveID) {     // is it for us ?

      BUGPR("RX+ slave received a for-me package\n");

      rxSlavePersonalPackage(msgSlaveBuf);
    }
    // HERE WE DO CLOCK SYNC STUFF
    else if (msgSlaveBuf.id ==  masterClockID) {

      if (msgSlaveBuf.buf[0] == 'T') { // time sync package just an extra check

        doClockSync(msgSlaveBuf);

      }
    }
  }
}


//------------------------------------------------------oOo---------------------------------------
void initAndStartCan0AndCan1()
{
  pinMode(28, OUTPUT);   // add on driver board for CAN0 enable pin (active LOW)
  pinMode(35, OUTPUT);  // add on driver board for CAN1 enable pin (active LOW)
  digitalWrite(28, LOW); // activate CAN bus transceiver/driver for can0
  digitalWrite(35, LOW); // activate 230 canbus transceiver for can1
  CANbus0.begin();      // intitalise and start CAN0 interface (to be used for master)
  CANbus1.begin();      // ... - slave interface
}
// -------------------------------------------------------------
// -------------------------------------------------------------
// ----------------------HERE WE START--------------------------
// -------------------------------------------------------------
// -------------------------------------------------------------

//------------------------------------------------------oOo---------------------------------------
void setup(void)
{
  pinMode(13, OUTPUT);  // our famous LED on pin 13
  Serial.begin(115200);
  delay(1000);  // let USb serial get time to be activated



  BUGPR("Teensy 3.6 dual CAN bus timer clock set test \n");

  initAndStartCan0AndCan1();

  BUGPR("gogo\n");
}


//------------------------------------------------------oOo---------------------------------------
void loop(void)
{
  // service as fast as possible - so NO delay in the functions !!! please :-)
  txMaster();
  rxMaster();
  rxSlave();
}


Jens