One Teensy 4.1 to send/receive information to six Teensy 4.1s

Status
Not open for further replies.
in the callback you can specify something like:
if ( msg.id == 0x123 ) //DoSomething
you can further read the bytes using msg.buf[x]
if ( msg.id == 0x123 && msg.buf[0] == 1 ) BlinkLed();

you can take a look at my ESP32 demo, the callback is identical to flexcan's as I designed it similarly. The callback will give you an idea of how to customize the data and what to do with it
https://github.com/tonton81/ESP32_CAN_GPIO/blob/main/ESP32_CAN_GPIO_SLAVE.ino
 
Last edited:
I got two Teensy 4.1 to send their respective frames and information to the receiver Teensy 4.1. The first sender sends information to the receiver that turns on LED A and turns off LED B. The second sender senders information that turns on LED B and turns off LED A. However, because the frames are queued. There is almost no delay between the first and second frame. Therefore, the LED A turns on and off really quickly. The context of this test is so I can control which frames I want and the output state of my machine.

Therefore I have a few questions. How do you know the amount of frames that are queued and their IDs? How do you remove the ones you don't need and can you remove specific frames by their ID?
 
anything you send gets out asap, you have no time to remove queues, even if your blasting out hundreds of frames :)

well if the receiver received data from both senders to toggle the same led, that will be the expected result, one tells the leds to switch states. maybe you should just have them use their own led, ledA for sender1 and ledB for sender2.

if you are talking about the receiving end, you'll need to decide which frames are useful and which can be ignored. the moment the callback exits that frame is lost, so queues deplete fast
 
When I use an if condition to choose a msg.ID, does it choose that frame? It seems like that is the case. When it does choose it, does the frame immediately get discarded?

If the frame doesn't come in the order I want; let's say I want frame ID-3, then ID-5 out of 7 but they come in 1523467 order, how do I get the frame in the order I want? For context, I want the frames to come in the order of let's say traffic lights: green-yellow-red. Do you just loop the if conditions until I get the order I want?
 
frames can be in any order if its from 2 or more nodes, if theyre both sending a same ID its hard to distinguish. if they have separate ids it's still unordered. This is not related to FIFO ordering which is different.

You would need to manage the receptions by timed events or state machines
in my demo i posted earlier i store relevant data in arrays and process the array later in the loop, if that helps
 
I understanding that FIFO is first in, first out. I would assume first frame and the first frame removed is how the it is implemented. How would this be useful in gathering or differentiating incoming frames?

I have worked with state machines in code and I might look into that. For receptions of timed events, how would you set the timing of incoming frames into timed events? What could would do that?

I was also thinking of relevant data in arrays and processing them later as well. My immediate idea was to whatever frame and its information that came in would be sent to an array. For example, if I have joint angle position data from Frame ID 0x101, I would store it in an array. The buffer in itself could have multiple categories of information. For example, the first 10 bits could be instructions for the receiving Teensy, and the rest is for storing data that can be analyzed later.
 
you have to understand that if 10 nodes are transmitting, the first frame to hit the receptor is the winner, so the order is never guarenteed. and you can't blast in 10 frames in front of others. You could use lower value IDs, since they have priority, however, giving them priority still doesn't guarentee ordering
 
That makes sense. I didn't know there were priorities in ID. I guess my confusion is if I have multiple incoming frames, I can loop through all of them as long as the callback hasn't exited yet. In that case, it sounds like a switch statement to store data would be the right approach here. Am I correct with these assumptions?
 
yes each callback fires once per reception, it doesn't loop, theyre meant to exit as fast as possible and the next frame is ready to fire.

you could store data from each node using IDs to identify them and later on process the data in your loop via switch statements, or depending on the data simplicity, running tasks directly if needed. if you do this though make sure your data is declared volatile since it is shared between the loop and the isr callback

in my car for example, i store data from the car's network, like speed, doors, brakes, gears, etc in the callback, and later on in my code if i need to use them for conditional checks i reference the variables in the conditions since they're always updated

so for example if you're storing an ADC value from 2 nodes from the callback, and checking which one is higher in the loop(), you can decide what to do based on that
 
the callback is from the interrupt context, you must declare your shared variables as volatile, example:

volatile uint8_t myVar = 0;
 
Thanks.

I need advice on storing information. So one Teensy is going to receive data continuously from all six Teensy. That one Teensy is going to store that data for in a non-volatile storage and I would like to store in a MicroSD. How would I go about doing that?
 
you'd need to store them in memory first and write them to sd as needed. you shouldn't be read/writing sd constantly for data fetching and modifying, there is lots of latency and overhead involved
 
the 4.1 has an sd slot on it, there should be demo examples in teensyduino, i never played with sd stuff yet
 
There is a microSD card slot on the teensy 4.1
Think there is some examples on the code part in the Arduino ide teensy examples.
 
the callback is from the interrupt context, you must declare your shared variables as volatile, example:

volatile uint8_t myVar = 0;

Perhaps in (maybe distant) future versions we could consider using EventResponder or some similar mechanism for callbacks to be done from main program context like yield() or a function which gets called from loop()?

Callbacks from interrupts are easy to implement and give lowest possible latency, but they subject people to some of the most difficult programming problems.
 
if they use events() in the loop() in flexcan_t4 it switches to queued buffering and the callbacks are fired from the loop(), not via interrupt context, declaration of volatile is not needed in this case
 
I'm running into an extremely odd issue. The Serial Monitor keeps restarting by itself when I run this code. At first I thought it was an issue with the Serial Monitor being overloaded by the sending Teensy to the receiving Teensy on the CAN Bus. However, I tried to run this code isolated on one Teensy and it still restarts itself after a few seconds. I am not quite sure why. Is there anything noticeably wrong with my code that would cause it to do this?

Code:
#include <FlexCAN_T4.h>
#include <iostream>
#include <vector>
#include <string.h>
#include <Servo.h>
using namespace std;
using std::vector;

FlexCAN_T4<CAN2, RX_SIZE_256, TX_SIZE_64> kneeT; // Create a constructor with CAN functionality called kneeT.

vector<int> messageData = {}; // This vector will record incoming messages' data code.

// below are the hip, knee, and ankle average gait cycles for both legs across multiple patients

vector<double> hipData = { 36.45843373, 36.34337349, 36.21204819, 35.81927711, 34.88192771, 33.43493976, 31.65421687, 29.63674699, 27.49638554, 25.32108434, 
23.15722892, 20.98493976, 18.78795181, 16.57650602, 14.35421687, 12.18313253, 10.08012048, 8.055421687, 6.112048193, 4.222891566, 2.389759036, 0.6295180723, 
-1.015060241, -2.506626506, -3.78373494, -4.742168675, -5.255421687, -5.195783133, -4.463253012, -2.888554217, -0.3813253012, 2.972891566, 6.884337349, 11,
15.06686747, 18.93192771, 22.48253012, 25.68012048, 28.47168675, 30.87590361, 32.91566265, 34.6, 35.91686747, 36.85240964, 37.3873494, 37.52168675, 37.28253012, 
36.84096386, 36.40361446, 36.11506024, 35.96204819 };

vector<double> kneeData = { 5.518072289, 7.887951807, 11.02771084, 14.40662651, 17.21204819, 18.94578313, 19.66445783, 19.55722892, 18.9186747, 17.99216867, 16.90421687, 
15.70963855, 14.46325301, 13.20783133, 11.9873494, 10.82710843, 9.759036145, 8.775903614, 7.91746988, 7.225301205, 6.796987952, 6.724096386, 7.123493976, 8.042168675,
9.525903614, 11.63373494, 14.44638554, 18.0746988, 22.60301205, 28.00542169, 34.11626506, 40.60301205, 46.95180723, 52.51024096, 56.75963855, 59.41927711, 60.49216867,
60.04518072, 58.17951807, 55.04216867, 50.76445783, 45.53313253, 39.51686747, 32.90722892, 25.95421687, 19.00421687, 12.61204819, 7.522891566, 4.478915663, 3.826506024,
5.181325301 };

vector<double> ankleData = { -2.145180723, -4.090963855, -5.821084337, -5.834939759, -4.324698795, -2.265060241, -0.2759036145, 1.428915663, 2.871686747, 4.134939759, 5.219277108,
6.124096386, 6.860843373, 7.481325301, 8.036144578, 8.569277108, 9.094578313, 9.593975904, 10.05903614, 10.48192771, 10.8626506, 11.1939759, 11.43373494, 11.45963855,
11.08614458, 10.08614458, 8.101807229, 4.703614458, -0.4301204819, -7.043975904, -13.85180723, -18.85060241, -20.57108434, -19.22710843, -16.18313253, -12.66506024, -9.265662651,
-6.192168675, -3.589759036, -1.497590361, 0.05542168675, 1.087951807, 1.580722892, 1.58313253, 1.137951807, 0.4355421687, -0.3343373494, -0.9879518072, -1.426506024, -1.822289157, 
-2.802409639 };

// We need to convert the data from doubles to integers because our servo motors cannot read the data precisely
// Therefore, integer type vectors with empty elements are made. Vectors can automatically resize.

vector<int> hip = {};
vector<int> knee = {};
vector<int> ankle = {};

// Set the offsets for your starting motor angle, since the motors may not all start at the same position.
int hipOffset = 90;
int kneeOffset = 90;
int ankleOffset = 90;

Servo KneeMotor; // We are creating a constructor called "Knee"
int controlPin = 2; // The data pin from the Teensy to Servo Motor is 2.

// Gait Cycle Stuff goes here:

int cycle = 0; // Start the cycle count at zero.
int gaitType = 1; // Placeholder rn but 1 = normal and all other numbers are abnormal gaits.
int gaitDuration = 1000; // Apparently the average gait cycle for men is between 0.97 to 1.08s. Here, we'll just the since number of 1000 to represent 1 second.
int gaitInterval = gaitDuration / kneeData.size(); // This provides the time between each data point of a gait cycle.

// Since there is a dumb mismatch, we need to setup a ratio to make sure the servo motors actually go to the correct angle.
double motorRatio = 0.6; // Right now, 180 is actually 300 for the servo motor, which means 180/360 = 0.6.

int whiteLED = 33; // Pin for white LED.

void setup(void) {
  Serial.begin(115200); // You want a high baud rate to make sure you pick up all the information you need.
  // Teensy 4.1 is fast enough that there won't be stability issues.
  delay(400); // A moment for the serial to set up before CAN protocols.
  kneeT.begin();
  kneeT.setBaudRate(500000);
  kneeT.setMaxMB(16);
  kneeT.enableFIFO();
  kneeT.enableFIFOInterrupt();
  kneeT.onReceive(canSniff);
  kneeT.mailboxStatus();
  kneeT.enableMBInterrupts();
  pinMode(whiteLED, OUTPUT); // Sets the white LED as an output.

  KneeMotor.attach(controlPin, 500, 2500); // Allows the MCU to equate the "Knee" to the physical Knee Servo Motor. Also, the 500 is 0 degrees and 2500 is 300 degrees.

  for(int i = 0; i < hipData.size(); i++) { 
    // here the double vectors from the original data are converted to integers
    // every loop changes one element and pushes it back to the new integer vector
    hip.push_back(int(hipData[i]));
    knee.push_back(int(kneeData[i]));
    ankle.push_back(int(ankleData[i]));
  }
  
  // We are using the knee motor here as our example:  
  KneeMotor.write(motorRatio * (hipData[0] + kneeOffset));

  // uncomment the below section if you need to confirm if the data is indeed the same size and to show the data
  /*
  for(int i = 0; i < hip.size(); i++){
    Serial.print(hip[i]);
    Serial.print(" ,");
  }
  Serial.println();
  Serial.println(hip.size());
  for(int i = 0; i < knee.size(); i++){
    Serial.print(knee[i]);
    Serial.print(" ,");
  }
  Serial.println();
  Serial.println(knee.size());
  for(int i = 0; i < ankle.size(); i++){
    Serial.print(ankle[i]);
    Serial.print(" ,");
  }
  Serial.println();
  Serial.println(ankle.size());
  delay(2000); // Time for user to check the readings first before the rest of the program runs
  */

  delay(1000); // Give the motor 1 second to get into position before we run it.

}

void canSniff(const CAN_message_t &msg) {
  Serial.print("MB "); Serial.print(msg.mb);
  Serial.print("  OVERRUN: "); Serial.print(msg.flags.overrun);
  Serial.print("  LEN: "); Serial.print(msg.len);
  Serial.print(" EXT: "); Serial.print(msg.flags.extended);
  Serial.print(" RTR: "); Serial.print(msg.flags.remote);
  Serial.print(" TS: "); Serial.print(msg.timestamp);  
  Serial.print(" ID: "); Serial.print(msg.id, HEX);
  Serial.print(" Buffer: ");
  for ( uint8_t i = 0; i < msg.len; i++ ) {
    if ( msg.id == 0x101 ) {
      Serial.print(msg.buf[i], HEX); Serial.print(" ");
      messageData.push_back(msg.buf[i]);
      Serial.print(messageData.back()); Serial.print(" ");
    }
  } Serial.println();
  static uint32_t _time = millis();
  Serial.print("Time between frames: ");
  Serial.println(millis() - _time);
  _time = millis();
}


void loop() {
  for(int i = 0; i < knee.size(); i++) {
    if (messageData.back() == 0) {
      i = i - 1;
      digitalWrite(whiteLED,LOW);
    } else {
      digitalWrite(whiteLED,HIGH);
    }
    
    KneeMotor.write(motorRatio * (hip[i] + kneeOffset));
    //Serial.print(hip[i]); // Print the angle the interval and motor output shaft is at.
    //Serial.print(", ");
    delay(gaitInterval*4); // The motor has trouble catching up to speed, since it takes one second to reach 60 degrees and the knee gait does it in 0.5 seconds.
  }
  Serial.println();
  Serial.print("Next Cycle is ");
  Serial.print(cycle); // Will print out which cycle this is.
  Serial.println();
  
  // This is optional but it's demonstrate that the cycle has ended and is returning the cycle back to its starting position.
  /*
  delay(2000); 
  KneeMotor.write(motorRatio * (hip[0] + kneeOffset));
  delay(2000); // Once the motor returns to its original position, the motor can start the gait cycle again.
  */
  
}
 
I usually just comment out stuff until the crash stops, but back when we did MST library we initially started with vectors, and they intermittantly crashed teensy 3.x as well. I see you always pushing into messageData, and never poping, are you sure you're not pushing your way out of available ram?
 
The messageData doesn't seem to be the issue here cause it's not receiving any new elements, since it is based on the condition of a sent frame. But it's a good idea for me to add popping once it exceeds a certain threshold.
 
I fixed the problem but I don't understand why it worked. When I set the messageData vector to include {0} instead of {}, the code runs without resetting itself. My guess is that the dot operator .back() in the if function doesn't know what to point when the vector has no elements. I'm not sure how or if that triggered the constant resets.
 
I ran into another problem. I'm trying to send the frames every two seconds, but it is sent every four seconds as read by the receiving serial monitor. And the frames are sent twice with no delay. I'm not sure what the logic error I have in my sending code is.

Screenshot 2022-03-20 104818.jpg


Sending Code:
Code:
// This code is used to send information from Teensy with a CAN Bus towards
// a Teensy with a CAN Bus.

#include <FlexCAN_T4.h>
#include <elapsedMillis.h>

FlexCAN_T4<CAN2, RX_SIZE_256, TX_SIZE_128> primaryT; // Create a constructor with CAN functionality called primaryT.
elapsedMillis tsk1000msCounter;
CAN_message_t message;
int greenLED = 15; // Pin for green LED
int redLED = 14; // Pin for red LED
int state = 0; // To control the amount of messages sent from Primary to receivers.

void setup() {
  primaryT.begin();
  primaryT.setClock(CLK_60MHz);
  primaryT.setBaudRate(500000);
  primaryT.setMaxMB(10);
  primaryT.setMB(MB0, RX);   /* Set Mailbox RX Direction */
  primaryT.setMB(MB1, RX);   /* Set Mailbox RX Direction */
  primaryT.setMB(MB2, RX);   /* Set Mailbox RX Direction */
  for (int i = 4; i < 10; i++) primaryT.setMB((FLEXCAN_MAILBOX)(i), TX); /* Set Mailbox TX Direction */

  message.id = 0x101; // This is the ID for the frame. Primary = 101; HipR = 102; HipL = 103; KneeR = 104; KneeL = 105; AnkleR = 106; AnkleL = 107;
  message.len = 5; // Data length

  Serial.begin(115200); delay(1000);
  primaryT.onReceive(canSniff);
  primaryT.enableMBInterrupts();
  Serial.print("primaryT Setup: ");
  primaryT.mailboxStatus();
  pinMode(greenLED,OUTPUT);
  pinMode(redLED,OUTPUT);
}

void loop() {
  /*
    if (state == 0) {
      state = 1;
      message.buf[0] = 1; // After 10 seconds, let the motor run.
      digitalWrite(greenLED,HIGH); // Turns the green LED on.
      digitalWrite(redLED,LOW); // Turns the red LED off.
    } else {
      message.buf[0] = 0; // After 10 seconds, pause the motor.
      state = 0;
      digitalWrite(greenLED,LOW); // Turns the green LED off.
      digitalWrite(redLED,HIGH); // Turns the red LED on.
    }
    primaryT.write(message);
    delay(5000);
  */

  if (tsk1000msCounter >= 2000) {
    tsk1000msCounter = 0;  // Reset millisecond timer
    if (state == 0) {
      state = 1;
      message.buf[0] = 1; // After X seconds, let the motor run.
      digitalWrite(greenLED,HIGH); // Turns the green LED on.
      digitalWrite(redLED,LOW); // Turns the red LED off.
    } else {
      message.buf[0] = 0; // After X seconds, pause the motor.
      state = 0;
      digitalWrite(greenLED,LOW); // Turns the green LED off.
      digitalWrite(redLED,HIGH); // Turns the red LED on.
    }
    primaryT.write(message); // Sends the message to CAN
  }

  
}

void canSniff(const CAN_message_t &msg) {
  Serial.print("MB "); Serial.print(msg.mb);
  Serial.print("  OVERRUN: "); Serial.print(msg.flags.overrun);
  Serial.print("  LEN: "); Serial.print(msg.len);
  Serial.print(" EXT: "); Serial.print(msg.flags.extended);
  Serial.print(" RTR: "); Serial.print(msg.flags.remote);
  Serial.print(" TS: "); Serial.print(msg.timestamp);
  Serial.print(" ID: "); Serial.print(msg.id, HEX);
  Serial.print(" Buffer: ");
  for ( uint8_t i = 0; i < msg.len; i++ ) {
    Serial.print(msg.buf[i], HEX); Serial.print(" ");
  } Serial.println();
  static uint32_t _time = millis();
  Serial.print("Time between frames: ");
  Serial.println(millis() - _time);
  _time = millis();
}


Receiving Code:
Code:
#include <FlexCAN_T4.h>
#include <iostream>
#include <vector>
#include <string.h>
#include <Servo.h>
using namespace std;
using std::vector;

FlexCAN_T4<CAN2, RX_SIZE_256, TX_SIZE_64> kneeT; // Create a constructor with CAN functionality called kneeT.

vector<int> messageData = {0}; // This vector will record incoming messages' data code.

// below are the hip, knee, and ankle average gait cycles for both legs across multiple patients

vector<double> hipData = { 36.45843373, 36.34337349, 36.21204819, 35.81927711, 34.88192771, 33.43493976, 31.65421687, 29.63674699, 27.49638554, 25.32108434, 
23.15722892, 20.98493976, 18.78795181, 16.57650602, 14.35421687, 12.18313253, 10.08012048, 8.055421687, 6.112048193, 4.222891566, 2.389759036, 0.6295180723, 
-1.015060241, -2.506626506, -3.78373494, -4.742168675, -5.255421687, -5.195783133, -4.463253012, -2.888554217, -0.3813253012, 2.972891566, 6.884337349, 11,
15.06686747, 18.93192771, 22.48253012, 25.68012048, 28.47168675, 30.87590361, 32.91566265, 34.6, 35.91686747, 36.85240964, 37.3873494, 37.52168675, 37.28253012, 
36.84096386, 36.40361446, 36.11506024, 35.96204819 };

vector<double> kneeData = { 5.518072289, 7.887951807, 11.02771084, 14.40662651, 17.21204819, 18.94578313, 19.66445783, 19.55722892, 18.9186747, 17.99216867, 16.90421687, 
15.70963855, 14.46325301, 13.20783133, 11.9873494, 10.82710843, 9.759036145, 8.775903614, 7.91746988, 7.225301205, 6.796987952, 6.724096386, 7.123493976, 8.042168675,
9.525903614, 11.63373494, 14.44638554, 18.0746988, 22.60301205, 28.00542169, 34.11626506, 40.60301205, 46.95180723, 52.51024096, 56.75963855, 59.41927711, 60.49216867,
60.04518072, 58.17951807, 55.04216867, 50.76445783, 45.53313253, 39.51686747, 32.90722892, 25.95421687, 19.00421687, 12.61204819, 7.522891566, 4.478915663, 3.826506024,
5.181325301 };

vector<double> ankleData = { -2.145180723, -4.090963855, -5.821084337, -5.834939759, -4.324698795, -2.265060241, -0.2759036145, 1.428915663, 2.871686747, 4.134939759, 5.219277108,
6.124096386, 6.860843373, 7.481325301, 8.036144578, 8.569277108, 9.094578313, 9.593975904, 10.05903614, 10.48192771, 10.8626506, 11.1939759, 11.43373494, 11.45963855,
11.08614458, 10.08614458, 8.101807229, 4.703614458, -0.4301204819, -7.043975904, -13.85180723, -18.85060241, -20.57108434, -19.22710843, -16.18313253, -12.66506024, -9.265662651,
-6.192168675, -3.589759036, -1.497590361, 0.05542168675, 1.087951807, 1.580722892, 1.58313253, 1.137951807, 0.4355421687, -0.3343373494, -0.9879518072, -1.426506024, -1.822289157, 
-2.802409639 };

// We need to convert the data from doubles to integers because our servo motors cannot read the data precisely
// Therefore, integer type vectors with empty elements are made. Vectors can automatically resize.

vector<int> hip = {};
vector<int> knee = {};
vector<int> ankle = {};

// Set the offsets for your starting motor angle, since the motors may not all start at the same position.
int hipOffset = 90;
int kneeOffset = 90;
int ankleOffset = 90;

Servo KneeMotor; // We are creating a constructor called "Knee"
int controlPin = 2; // The data pin from the Teensy to Servo Motor is 2.

// Gait Cycle Stuff goes here:

int cycle = 0; // Start the cycle count at zero.
int gaitType = 1; // Placeholder rn but 1 = normal and all other numbers are abnormal gaits.
int gaitDuration = 1000; // Apparently the average gait cycle for men is between 0.97 to 1.08s. Here, we'll just the since number of 1000 to represent 1 second.
int gaitInterval = gaitDuration / kneeData.size(); // This provides the time between each data point of a gait cycle.

// Since there is a dumb mismatch, we need to setup a ratio to make sure the servo motors actually go to the correct angle.
double motorRatio = 0.6; // Right now, 180 is actually 300 for the servo motor, which means 180/360 = 0.6.

int whiteLED = 33; // Pin for white LED.

void setup(void) {
  Serial.begin(115200); // You want a high baud rate to make sure you pick up all the information you need.
  // Teensy 4.1 is fast enough that there won't be stability issues.
  delay(400); // A moment for the serial to set up before CAN protocols.
  kneeT.begin();
  kneeT.setBaudRate(500000);
  kneeT.setMaxMB(16);
  kneeT.enableFIFO();
  kneeT.enableFIFOInterrupt();
  kneeT.onReceive(canSniff);
  kneeT.mailboxStatus();
  kneeT.enableMBInterrupts();
  pinMode(whiteLED, OUTPUT); // Sets the white LED as an output.

  KneeMotor.attach(controlPin, 500, 2500); // Allows the MCU to equate the "Knee" to the physical Knee Servo Motor. Also, the 500 is 0 degrees and 2500 is 300 degrees.

  for(int i = 0; i < hipData.size(); i++) { 
    // here the double vectors from the original data are converted to integers
    // every loop changes one element and pushes it back to the new integer vector
    hip.push_back(int(hipData[i]));
    knee.push_back(int(kneeData[i]));
    ankle.push_back(int(ankleData[i]));
  }
  
  // We are using the knee motor here as our example:  
  KneeMotor.write(motorRatio * (hipData[0] + kneeOffset));

  // uncomment the below section if you need to confirm if the data is indeed the same size and to show the data
  /*
  for(int i = 0; i < hip.size(); i++){
    Serial.print(hip[i]);
    Serial.print(" ,");
  }
  Serial.println();
  Serial.println(hip.size());
  for(int i = 0; i < knee.size(); i++){
    Serial.print(knee[i]);
    Serial.print(" ,");
  }
  Serial.println();
  Serial.println(knee.size());
  for(int i = 0; i < ankle.size(); i++){
    Serial.print(ankle[i]);
    Serial.print(" ,");
  }
  Serial.println();
  Serial.println(ankle.size());
  delay(2000); // Time for user to check the readings first before the rest of the program runs
  */

  delay(1000); // Give the motor 1 second to get into position before we run it.

}

void canSniff(const CAN_message_t &msg) {
  Serial.println();
  Serial.print("MB "); Serial.print(msg.mb);
  Serial.print("  OVERRUN: "); Serial.print(msg.flags.overrun);
  Serial.print("  LEN: "); Serial.print(msg.len);
  Serial.print(" EXT: "); Serial.print(msg.flags.extended);
  Serial.print(" RTR: "); Serial.print(msg.flags.remote);
  Serial.print(" TS: "); Serial.print(msg.timestamp);  
  Serial.print(" ID: "); Serial.print(msg.id, HEX);
  Serial.print(" Buffer: ");
  for ( uint8_t i = 0; i < msg.len; i++ ) {
    
    if ( msg.id == 0x101 ) {
      Serial.print(msg.buf[i], HEX); Serial.print(" ");
      messageData[0] = msg.buf[i];
      Serial.print(messageData.back()); Serial.print(" ");
    }
    
  } 
  Serial.println();
  static uint32_t _time = millis();
  Serial.print("Time between frames: ");
  Serial.println(millis() - _time);
  _time = millis();
}


void loop() {
  
  for(int i = 0; i < knee.size(); i++) {
    
    if (messageData[0] == 0) {
      i = i - 1;
      digitalWrite(whiteLED,LOW);
    } else {
      digitalWrite(whiteLED,HIGH);
    }
    
    KneeMotor.write(motorRatio * (hip[i] + kneeOffset));
    Serial.print(messageData[0]); // Print the message stored at the end of the messageData vector.
    //Serial.print(hip[i]); // Print the angle the interval and motor output shaft is at.
    Serial.print(", "); // For ease of reading.
    delay(gaitInterval*4); // The motor has trouble catching up to speed, since it takes one second to reach 60 degrees and the knee gait does it in 0.5 seconds.
    
  }
  Serial.println();
  Serial.print("Next Cycle is ");
  Serial.print(cycle); // Will print out which cycle this is.
  Serial.println();
  
  // This is optional but it's demonstrate that the cycle has ended and is returning the cycle back to its starting position.
  /*
  delay(2000); 
  KneeMotor.write(motorRatio * (hip[0] + kneeOffset));
  delay(2000); // Once the motor returns to its original position, the motor can start the gait cycle again.
  */
  
}
 
Code:
kneeT.enableMBInterrupts();
This is redundant, by default theres no RX mailboxes when FIFO is enabled unless you specify them to be RX as well.

Should be fine, I'm running the same library here and only sending and receiving 1x frame on my end. Have you checked your transceiver terminations if they are correct? the controllers try to resend a frame if the don't get a proper ACK on transmission. Try lowering the rate to maybe 125k as a test, you can get away with running low rates with/without terminations
 
Status
Not open for further replies.
Back
Top