TeensyCAN

Status
Not open for further replies.

tonton81

Well-known member
Here it is! CAN2.0/CANFD Implementation early stages. Redesign of CANquitto. This library has the ability not only to work on CAN2.0 and CANFD, but the payload transfers are automatically adjusted to the dlc size of your mailbox and sent out dissambled, and at the other endpoint, they are re-assembled based on the DLC implementation.

Library supports sending payloads of theoretically 4,063,170 bytes. Considering the RAM on MCU, I doubt you'd reach this ;)
New feature not introduced in CANquitto ---> broadcasting payloads! All nodes can receive the same identical payload, sort of like UDP, no responses are needed.
Payloads sent specifically to nodes, will return an ACK 0x06 if the reception and CRC are validated from other endpoint.

Uses the background functions built into FlexCAN_T4 to receive frame data.
Examples were posted with the library. To be effective, at least one or more extended mailboxes need to be interrupt enabled in FlexCAN_T4.

https://github.com/tonton81/TeensyCAN

On a side note only FD was tested, with mailbox sizes of 8 to 64 bytes in the slave demo, the master demo was set to 64 bytes to demonstrate it can accept all different DLC lengths of the slave transmissions

Node.sendMsg() sends a broadcast payload to all nodes
node100.sendMsg() sends a payload specifically to node #100
 
Last edited:
Upped to github is a new update to TeensyCAN. USB Serial & UART accesses can be called specifically to a node or globally on the network of nodes!

Node.Serial.println("Hello World!"); // prints to Serial monitor of all nodes on the network, using a single set of data frames. All nodes will assemble the message and process as needed.
node100.Serial.println("Hello World!"); // prints specifically on this node, other nodes ignore the call

The supported functions are write(), write(buf,len), print(), println(), read(), readBytes(), peek(), available().
Yes! This new update has a 2 way send and receive payloads in a single call, and the commands are not ran from an interrupt!
Whats more? 2 or more nodes may print to the same port without collision if arrays are used. The accesses are sequential and responded to immediately.

Note, do not try to print() stuff to other serial ports if you didn't run Serialx.begin() on that node, it will hard lock the mcu.

Later on when I add GPIO support, it might be a good solution for people who want to keep gpio pins sync'd accross microcontrollers. Example is (not implemented yet):

Node.pinMode(13, OUTPUT); /* tell all nodes to set their pin 13 as output */
Node.digitalWrite(13,HIGH); /* tell all nodes to turn their leds on */
node100.digitalWrite(13,LOW); /* while all other nodes have pin13 HIGH, node100 only is switched LOW */

On a side note, global commands have no responses. Example:
Node.Serial.println("Hello World!"); /* dont expect valid return value */
node100.Serial.println("Hello World!"); /* returns 13 */
 
New update for TeensyCAN.

Node Discovery! The library is able to keep a list of active nodes, their DLC capabilites (receive DLC size max), and their timeout on the network (millis()).
The timeout depicts how long before the node can resend a keep-alive signal on the bus, and the limit is kept on the sender's active list while it's checked against the local timestamp.
DLC sizing, the node on the active list has his max DLC capability stored. This prevents 64 byte nodes from writing 64 byte data to 32byte DLC nodes. The sender node now sends at the receivers's size, in this case, 32 bytes. If, however, the receiver accepts a bigger DLC than the sender, the sender sends at his capability instead. So a 32byte node would send a 32byte buffer to a 64byte node. This is all automatic and no user intervention is needed. As far as global sends go, they go out based on the smallest DLC node. Basically, if you have 5 nodes on the network, they will be on the active list.
Let's say:

node1 is 32bytes,
node2 is 16bytes,
node3 is 64bytes,
node4 is 16bytes,
node5 is 8bytes.

Global sends would go out as 8 bytes traffic. However, if node5 were to drop off the list (offline), global sends would now be sent as 16 byte traffic.

sendMsg has been upgraded to use the unified assembler/disassembler as the commands. It's features have been retained.
 
Last edited:
TeensyCAN has now gone multi-bus mode! Nodes can be talked to on any or all of the 3 CAN busses on Teensy. Responses to nodes will happen to their appropriate busses. Globals now send to all 3 busses unless one is chosen specifically using ::setBus.

If setBus is set, only one bus is broadcasted to. If unset, all busses will be talked to for global messages. (discoveries or node replies).

Using 64byte frames, 1024bytes transfers now at 2ms. This requries the FlexCAN_T4::events() update to return the sequential queues left in the buffer needed to send out, before a next TeensyCAN message went out. Previously, this test took 17ms to ensure no payload collisions, to transfer the same 1024 payload accross 64byte message frames.

Multibus mode retains FD controller TX sizes of external nodes:

If the sender is 64byte frames supported, and the FD node supports 32 max, The sender will send at 32 frame size. In the global scheme, all CAN2 busses, the sender would send out as 8 byte frames, while, maintaining the size of the FD bus node (32bytes), sending at it's supported size on the FD bus.
 
Hello! This library seems absolutely perfect for my project, when I get home from work I'm going to get a few teensys and try it out!

I have a few questions that after looking around a bit I didn't see answered in the demos.

Can I send floats directly or do I have to break them up first? This would be useful for sending GPS data (lat,lon) as well as lots of sensor data in general for me.

(If you're curious, the project is an ultralight aircraft!)

I have about 8 nodes that are gonna be sending all kinds of things to each other, so your large payload system and node private messaging system is going to be a godsend for me since I was struggling to figure out how I was going to implement all of this.

Thank you again! I'm a bit of a noob at the higher level programming, I'm more of a mechanical person who knows enough coding to glue my projects together.
 
The CAN data is bytes only, you will need to handle the data in the array, splitting up the floats to bytes and back at other end. You could always use the PacketID of the data to identify which frames are identified uniquely as floats or other data, to process different types of arrays, you could also do some trickery I think to pass an array of floats as an array of bytes by using some cast magic, and adjusting the size of the array as necessary, but I am no expert on that :)
 
Sounds good! Thank you!

I'm so excited to have found this library. It simplifies so many things I thought I was going to have to do manually by myself!
 
Hello! I am having some trouble getting this working. I realized just now, though, that it is because CAN FD only works on port 3, and I am trying to use port 1. I know I can solder wires to the bottom, but I intend to put this in a socket on custom boards, so I don't want to have to use those non-pin-headered pins on the bottom.

I realize FD is faster, but I do not think I need the speed. How can I switch the example to work on port 1? I'm not sure which parts I need to remove to make it non FD.

Apologies again for the probably very noobie questions!

To elaborate, I have 2 teensy 4's hooked via CAN1 to two of these adapters:
https://www.amazon.com/gp/product/B00KM6XMXO/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&psc=1

And I don't seem to be getting anything.. I changed some stuff I think would work based on the github page for flexCAN to change to CAN1, but in the serial monitor on the slave all I get is a repeating '0' and that's it.

I'm really not sure what to do to get this working, without FD, I have not tried with FD however as I can't really easily use it in this setup. So far my CAN bus experience is with the MCP2515 boards and mcpCAN library, that did work.
 
Last edited:
you need to use FlexCAN_T4 constructor, not FlexCAN_T4FD, and use CAN1 instead of CAN3

Also, it's good to run one of the FlexCAN_T4 demos to confirm your connections are good before working on TeensyCAN as it relies on the driver to be communicating properly
 
I wanted to ask about that, actually. In mcpCAN demos there was a simple set of sketches to be able to transmit and receive on two different arduinos. I think I found the equivalent for FlexCAN_T4, this one:

https://github.com/tonton81/FlexCAN...rupts/CAN2.0_example_FIFO_with_interrupts.ino

So in theory I should be able to hook two teensies together and run this sketch on both and I should start seeing things on the serial console if everything is working?
 
yes' correct. well they receive, you need to put something to transmit so the other teensy can see it.

CAN_message_t msg;
msg.id = 0x123;
Can0.write(msg);
 
Heh. I feel dumb. I got FlexCAN_T4 working! Now to move on to TeensyCAN.

For anyone else suffering this fate, take note, it was dumb.

I probed the canbus lines with my oscilloscope and was wondering why the readings were so minute and low.. Didn't make any sense. Then I measured the resistance of the bus.. dead short. What?

So on the waveshare module, there is 2 pins sticking up. On the MCP2515 these were for a jumper to include or not include the built in 120 ohm resistor. So you could jumper the two end nodes to terminate the bus.

On the waveshare shield, these two pins are actually just CANL and CANH, you can use them instead of the screw terminals. I just shorted both sides of by bus and was wondering why it wasn't working. :mad: Doh!

Now it works fine. Don't do that.

Anyway, onwards to the actual library this thread is about!
 
I have data! Woohoo!

I guess now my only real question is.. is there any more fleshed out examples for this library? I can see that these two lines decide.. if the packet is being sent to ID 5, globally or ID7, locally..? (using Node.sendMsg instead of node100.sendMsg)

Code:
Serial.println(    Node.sendMsg(data, 40, 5)); /* Global always returns 1, no response. */
Serial.println(node100.sendMsg(data, 40, 7)); /* ACK: 0x06, TIMEOUT = 0xFF */

What I don't understand is what the difference is between sending data with node100.sendMsg vs Node.sendMsg. The FD_Master sketch receives both exactly the same, except I guess I can see the broadcast is 1 on Node.SendMsg, and broadcast is 0 on node100.sendMsg.

My main questions right now are: How do I target certain nodes to send data to? I get broadcast is to send it to everyone, but how do I decide to send data only to nodes.. lets say from node 2, to nodes 3 and 4, on an 8 node system? What would have to be done on both ends? On node100.sendMsg, I don't see anything relating to what node to send it to, just these three fields. node100.sendMsg(data(obvious),40(data length?),7(can packet ID)

I am super sorry for all these questions, I can usually figure all of this out on my own but there are not too many examples to follow, so this is all I have been able to do on my own thus far. Thank you so much for all the fast responses!
 
basically, Node sends to all nodes, its global, whereas your specific node100, only receives the data.

if you do Node.Serial.println("hello world");, all nodes will print that to Serial port.
if you do that to node100, it will print only to node100's Serial port.

the array transfers work the same way.
 
check the constructor for node100, that points to the node you want. on the other nodes, you add the nodes you want to control as well. this is a 2way library where they are all multi-master.

the nodes are automatically sending an active frame on a network that includes their payload size so they can dynamically talk to CAN and CANFD irregardless of data size, global messages would be checking the active list for the smallest DLC, which in CAN2.0 mode is 8 in your network, so they will automatically adjust based on that, which is why FD is faster, because TeensyCAN could easily see a 64 byte data node and send a frame to it 64bytes at a time provided the sender can send at that size, else it sends at it's max

PacketID is an identifyer of the array being received, so you know what to do with it. lets say packetid 5 is sensor data and packetid 8 is gps data, you can handle the arrays that way
 
TeensyCAN node36 = TeensyCAN(36);
if you have nodes 2 3 8,

on node 2, put:
TeensyCAN node3 = TeensyCAN(3);
TeensyCAN node8 = TeensyCAN(8);

on node 3, put:
TeensyCAN node2= TeensyCAN(2);
TeensyCAN node8 = TeensyCAN(8);

etc that will allow each node to master each other
 
Okay! How do I set the ID of each node, though? On your slave example as I downloaded from github, this is the start of it:

Code:
TeensyCAN node100 = TeensyCAN(100);

void setup() {
  Serial.begin(115200); delay(400);
  pinMode(6, OUTPUT); digitalWrite(6, LOW); /* enable transceiver */
  FD.begin(); /* enable the FlexCAN controller before assigning it */

  node100.setBus(_CAN3);
  node100.setID(20);

From my understanding of what you are saying.. the first line of the code I posted, that's where I put all the OTHER nodes like you just said, with 2 and 8 for example, and the... node100.setID(20); is setting this node's ID?

Why is it node100.setID instead of Node.setID? Do I always use the first constructor as the node itself? In the master it's just Node.setID. Which is the right way to do it?

Thanks so much for the answers!
 
Oh okay. Man, I'm learning so many new things!

Thank you! I'll go scrounge up a third node to try and start really getting this test going.

When you were saying about how FD was faster then 2.0.. you mentioned packet size.

I thought CAN2.0 was able to handle the 64byte packets too? Or is that FD only and I misunderstood?

If all of my nodes are teensy4's running can2.0, will I get the 64 byte packets? Or will it shrink to 8?
 
it will automatically adjust to the size on the network.
lowest DLC to support global nodes, DLC of other node if capable of sending to it's size, otherwise it will use lower size automatically. CANFD is "up to" 64 bytes, CAN2.0 is 8 bytes max.
Note: on CANFD, using 8 byte message boxes gives you up to 64 mailboxes, while using 64 byte sizes gives you around 14. CANFD is also up to 8x faster in the data field, arbitration remains same speed as CAN2.0. 1Mbps nominal max, 8Mbps data max
 
Ok, understood!

My can transceivers only support up to 1MBPS anyway. What I will likely do is get all 8 nodes set up and see if they can really send all the data to each other or if it will be a speed issue..

Last question for now. Is it possible to target multiple nodes with the same message at the same time so I don't have to transmit twice? For example: Engine data unit data is relevant to the main flight computer as well as the backup display/datalogger. Right now, it seems like I have to send one transmission for each node. Broadcast would work but then it kind of defeats the point of this private messaging system in the first place, yeah?
 
Excellent. Thank you!

Is there any other functions this library provides other then sending large packets and the whole node private/broadcast messaging system with acks and whatnot? (and I saw earlier you said I can do remote serial writes, that's pretty cool!)

Also, I'm trying to understand by reading through other stuff, what does Can0.Events(); and Node.events(); do? How often do they have to run? I see them in just the main loop, how long can I leave them for before bad things start to happen, if my code goes off in another direction for a while before coming back to main loop?
 
Serial was implemented, there was more to be implemented, like remote I2C, SPI, analog, digital pin access. what i liked about the global method is for synchronizing multiple MCU gpio pins over a single message. Node events() needs to be run to process a reassembled array. Can0.events() is for flexcan itself, needed for transmits to work.

if you want to see what it was based upon, actually partly, took a rewrite, CanQuitto was the original T3.x version for CAN2.0 only
 
Status
Not open for further replies.
Back
Top