Using the CAN Bus BFF with Arduino involves wiring up the BFF to your Arduino-compatible QT Py or Xiao form factor board, installing the Adafruit_MCP2515 library, and running the provided example code. In the example below, you'll connect a RP2040 CAN Bus Feather to a QT Py plugged into a CAN Bus BFF. The Feather will send CAN Bus messages whenever the seesaw rotary encoder is used and the QT Py will receive and display the messages on a STEMMA OLED.
Wiring
Plug a CAN Bus BFF into your QT Py or Xiao form factor board exactly as shown below. Here's an example of connecting a QT Py RP2040 to the BFF.
Connect the QT Py RP2040 with plug headers into the CAN Bus BFF with socket headers. They should be plugged in with the backs of the boards facing each other.
For more information on soldering socket headers, check out this Learn Guide.
Then, you'll connect the Feather and BFF CAN Bus connections to each other, followed by the STEMMA peripherals to the corresponding boards.
QT Py
-
Board STEMMA 3V to screen VIN (red wire)
-
Board STEMMA GND to screen GND (black wire)
-
Board STEMMA SCL to screen SCL (yellow wire)
- Board STEMMA SDA to screen SDA (blue wire)
CAN Bus
- Feather H terminal block to BFF H JST-PH (red wire)
- Feather middle (ground) terminal block to BFF ground JST-PH (black wire)
- Feather L terminal block to BFF L JST-PH (white wire)
Feather RP2040 CAN Bus
-
Feather STEMMA 3V to rotary encoder VIN (red wire)
-
Feather STEMMA GND to rotary encoder GND (black wire)
-
Feather STEMMA SCL to rotary encoder SCL (yellow wire)
- Feather STEMMA SDA to rotary encoder SDA (blue wire)
Library Installation
You can install the Adafruit MCP2515 library for Arduino using the Library Manager in the Arduino IDE.
Click the Manage Libraries ... menu item, search for Adafruit MCP2515 and select the Adafruit MCP2515 library:
If asked about dependencies for any of the libraries, click "Install all".
If the "Dependencies" window does not come up, then you already have the dependencies installed.
Follow the same process for the Adafruit seesaw Library...
...Adafruit NeoPixel library...
...and the Adafruit SSD1306 library.
// SPDX-FileCopyrightText: 2024 Limor Fried for Adafruit Industries // // SPDX-License-Identifier: MIT #include "Adafruit_seesaw.h" #include <seesaw_neopixel.h> #include <Adafruit_MCP2515.h> #include <Adafruit_NeoPixel.h> #define CS_PIN PIN_CAN_CS // for Feather RP2040 CAN, change if needed! Adafruit_MCP2515 mcp(CS_PIN); #define CAN_BAUDRATE (250000) // must match the other devices! #define SS_SWITCH 24 #define SS_NEOPIX 6 #define SEESAW_ADDR 0x36 Adafruit_seesaw ss; seesaw_NeoPixel sspixel = seesaw_NeoPixel(1, SS_NEOPIX, NEO_GRB + NEO_KHZ800); Adafruit_NeoPixel pixel(1, 21, NEO_GRB + NEO_KHZ800); int16_t encoder_position; bool button_state; void setup() { Serial.begin(115200); delay(500); while (!Serial) delay(10); Serial.println("MCP2515 Sender test!"); if (!mcp.begin(CAN_BAUDRATE)) { Serial.println("Error initializing MCP2515."); while(1) delay(10); } Serial.println("MCP2515 chip found"); Serial.println("Looking for seesaw!"); if (! ss.begin(SEESAW_ADDR) || ! sspixel.begin(SEESAW_ADDR)) { Serial.println("Couldn't find seesaw on default address"); while(1) delay(10); } Serial.println("seesaw started"); uint32_t version = ((ss.getVersion() >> 16) & 0xFFFF); if (version != 4991){ Serial.print("Wrong firmware loaded? "); Serial.println(version); while(1) delay(10); } Serial.println("Found Product 4991"); // set not so bright! sspixel.setBrightness(20); sspixel.show(); // ditto built in pixel pinMode(NEOPIXEL_POWER, OUTPUT); digitalWrite(NEOPIXEL_POWER, HIGH); pixel.begin(); pixel.setBrightness(20); pixel.show(); // use a pin for the built in encoder switch ss.pinMode(SS_SWITCH, INPUT_PULLUP); // get starting position encoder_position = ss.getEncoderPosition(); button_state = ss.digitalRead(SS_SWITCH); } void loop() { bool new_button = ss.digitalRead(SS_SWITCH); if (new_button != button_state) { Serial.println("Button pressed!"); } int32_t new_position = ss.getEncoderPosition(); // did we move around or button change? if ((new_button != button_state) || (encoder_position != new_position)) { encoder_position = new_position; // and save for next round button_state = new_button; pixel.setPixelColor(0, Wheel(new_position & 0xFF)); pixel.show(); Serial.print("Sending Position "); Serial.print(encoder_position); Serial.print(" & Button "); Serial.println(button_state); mcp.beginPacket(0x12); if (ss.digitalRead(SS_SWITCH)) { mcp.write(0); } else { mcp.write(1); } mcp.write(encoder_position >> 8); mcp.write(encoder_position & 0xFF); if (mcp.endPacket()) { Serial.println("Done"); // change the neopixel color on success sspixel.setPixelColor(0, Wheel(new_position & 0xFF)); } else { Serial.println("Failure"); // turn off neopixel on failure sspixel.setPixelColor(0, 0x00); } sspixel.show(); } // don't overwhelm serial port delay(10); } uint32_t Wheel(byte WheelPos) { WheelPos = 255 - WheelPos; if (WheelPos < 85) { return sspixel.Color(255 - WheelPos * 3, 0, WheelPos * 3); } if (WheelPos < 170) { WheelPos -= 85; return sspixel.Color(0, WheelPos * 3, 255 - WheelPos * 3); } WheelPos -= 170; return sspixel.Color(WheelPos * 3, 255 - WheelPos * 3, 0); }
CAN Bus BFF Receiver
// SPDX-FileCopyrightText: 2024 Limor Fried for Adafruit Industries // // SPDX-License-Identifier: MIT #include <Adafruit_MCP2515.h> #include <Adafruit_SSD1306.h> #include <Fonts/FreeSans9pt7b.h> #define CS_PIN A3 #define CAN_BAUDRATE (250000) Adafruit_MCP2515 mcp(CS_PIN); Adafruit_SSD1306 oled(128, 64, &Wire1, -1); void setup() { Serial.begin(115200); while(!Serial) delay(10); Serial.println("MCP2515 OLED Receiver test!"); if (!mcp.begin(CAN_BAUDRATE)) { Serial.println("Error initializing MCP2515."); while(1) delay(10); } Serial.println("MCP2515 chip found"); if (!oled.begin(SSD1306_SWITCHCAPVCC, 0x3D)) { Serial.println(F("SSD1306 allocation failed")); while(1) delay(10); } oled.display(); delay(1000); oled.clearDisplay(); oled.display(); oled.setFont(&FreeSans9pt7b); oled.setTextColor(SSD1306_WHITE); } void loop() { // try to parse packet int packetSize = mcp.parsePacket(); if (packetSize) { // received a packet Serial.print("Received "); Serial.print("packet with id 0x"); Serial.print(mcp.packetId(), HEX); Serial.print(" and length "); Serial.println(packetSize); // only look for correct length packets if (packetSize != 3) return; // wait till all data is ready! while (mcp.available() != 3) { delay(1); } uint8_t button_state = 0; int16_t val = 0; button_state = mcp.read(); val = mcp.read(); val <<= 8; val |= mcp.read(); Serial.print("Button is "); if (button_state) Serial.print("pressed"); else Serial.print("released"); Serial.print(" & value is "); Serial.println(val); oled.clearDisplay(); oled.setCursor(0, 15); oled.println("CAN receiver"); oled.print("Val: "); oled.println(val); oled.print("Button "); if (button_state) oled.print("DOWN"); else oled.print("UP"); oled.display(); } }
Upload the Example Feather Code to the Feather RP2040 CAN Bus and then upload the Example CAN Bus BFF Code to the QT Py RP2040. When you open the Serial Monitor for the Feather, you'll see confirmations that packets have been sent every time you turn the rotary encoder or press the button. As you turn the rotary encoder, the NeoPixels on the encoder and the Feather will advance thru the color wheel.
When you open the Serial Monitor for the QT Py RP2040, you'll see the messages coming in via CAN for the status of the encoder position and the push button. You'll also see them on the OLED along with "CAN receiver" at the top.
Text editor powered by tinymce.