Skip to content

Commit

Permalink
readme and examples
Browse files Browse the repository at this point in the history
  • Loading branch information
Stefan Hirschenberger committed May 22, 2024
1 parent abcf733 commit 02ff170
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 174 deletions.
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
# Arduino library for interfacing Blink Marine PKP-3500-SI-MT via CANopen
<p align="center"><img src="docs/readmeHeaderImage.png" alt="PKPMCPDuino" width="400"/>
</p>
</br>
[![Lint source code](https://github.com/Flashmueller/PKP2600SI-Arduino-CAN-Controller/actions/workflows/lintSourceCode.yml/badge.svg)](https://github.com/Flashmueller/PKP2600SI-Arduino-CAN-Controller/actions/workflows/lintSourceCode.yml)
[![Build Arduino examples](https://github.com/Flashmueller/PKP2600SI-Arduino-CAN-Controller/actions/workflows/buildExamples.yml/badge.svg)](https://github.com/Flashmueller/PKP2600SI-Arduino-CAN-Controller/actions/workflows/buildExamples.yml)
[![Build Arduino examples](https://github.com/Flashmueller/PKP2600SI-Arduino-CAN-Controller/actions/workflows/buildExamples.yml/badge.svg)](https://github.com/Flashmueller/PKP2600SI-Arduino-CAN-Controller/actions/workflows/buildExamples.yml)</br></br>
This library enables control of a PKP-3500-SI-MT CAN Keypad using any CAN bus interface with an Arduino-compatible microcontroller. This project is a fork of [PKP2600SI-Arduino-CAN-Controller](https://github.com/Mbmatthews/PKP2600SI-Arduino-CAN-Controller), created to extend support to the PKP-3500-SI-MT model. Due to inconsistencies in the interface and varying hardware requirements, the library has been rewritten to be hardware-independent with minimal dependencies.

## Features
Expand All @@ -13,6 +12,13 @@ This library enables control of a PKP-3500-SI-MT CAN Keypad using any CAN bus in
- Callback Function: Customizable callback function for sending messages to the CAN network.
- Support for Multiple Models: Designed to support various Blink Marine Keypads. So far tested with PKP-3500-SI-MT only.

## Future Development
This library currently supports all basic functionalities of the keypads. However, these versatile devices have many more features that will be unlocked in future updates. If your application requires a functionality that is not yet available, please reach out or consider contributing to the library.
Next steps may include:
- Read out hardware and software identity data
- Encoder led blinking
- Examples for additional microcontroller boards

## Contributing
Contributions are highly appreciated. To contribute:

Expand All @@ -21,6 +27,8 @@ Contributions are highly appreciated. To contribute:
3. Commit your changes and push the branch.
4. Open a pull request describing your changes.

We use pre-commit hooks and clang-format to ensure code quality and maintain coding standards. Make sure to have these tools installed for compliance with our guidelines.

## License
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.

Expand Down
323 changes: 168 additions & 155 deletions examples/PkpInitialConfiguration/PkpInitialConfiguration.ino
Original file line number Diff line number Diff line change
@@ -1,184 +1,197 @@
// This sketch sends the proper CAN messages to configure a PKP2600SI in the way that is expected for the actual sketch to control it.

#include <mcp2515.h>

#define KEYPAD_CAN_ID 0x15
#define PKP_BAUDRATE_125k 0x04
#define PKP_BAUDRATE_250k 0x03
#define PKP_BAUDRATE_500k 0x02
#define PKP_BAUDRATE_1M 0x00

uint8_t keypadBaudrate = PKP_BAUDRATE_1M;
CAN_SPEED mcpBaudrate = CAN_1000KBPS; // should be same speed as above
CAN_CLOCK mcpClockSpeed = MCP_8MHZ;


MCP2515 mcp2515(10);
// This sketch provides a basic configuration for your keyboard without using the actual classes from the library

#define USE_MCP2515 // Comment this line to use Arduino CAN (Feather M4 CAN)

#include <SPI.h>
#ifdef USE_MCP2515
#include <mcp2515.h>

CS_PIN_MCP 10
MCP_CLOCK_SPEED MCP_8MHZ
#else
#include <Adafruit_CAN.h>
#endif

enum PKPBaudRate: uint8_t{
PKP_BAUDRATE_0020k = 0x07,
PKP_BAUDRATE_0050k = 0x06,
PKP_BAUDRATE_0125k = 0x04,
PKP_BAUDRATE_0250k = 0x03,
PKP_BAUDRATE_0500k = 0x02,
PKP_BAUDRATE_1000k = 0x00
};

//Configuration here:
#define KEYPAD_ACT_CAN_ID 0x15
#define KEYPAD_NEW_CAN_ID 0x16
#define KEYPAD_NEW_BAUD_RATE PKP_BAUDRATE_1000k;
#define KEYPAD_ACTVIE_AT_STARTUP 1 //1: enable, 0: disable
#define KEYPAD_STARTUP_LED_SHOW 1 //1: complete show (default), 0: disable, 2: fast flash
#define KEYPAD_HEARTBEAT_INTERVAL 500 //0: heartbeat production disabled, otherwise hearteat interval [ms]
#define KEYPAD_PERIODIC_TX_INTERVAL 0 //0: eventbased pdo transmission, otherwise pdo interval [ms]

#ifdef USE_MCP2515
MCP2515 mcp2515(CS_PIN_MCP, MCP_CLOCK_SPEED);
CAN_SPEED mcpBaudrate = CAN_1000KBPS;
CAN_CLOCK mcpClockSpeed = MCP_8MHZ;
#else
Adafruit_CAN can;
#endif

void setup() {
Serial.begin(115200);
// start MCP SPI comms, and check if things are actually working before continuing to config the MCP (when SPI is not working, this always returns
// ERROR_OK so this doesn't work)
bool MCP_OK = false;
while (!MCP_OK) {
if (mcp2515.reset() == MCP2515::ERROR_OK) {
Serial.println("MCP OK");
MCP_OK = true;
}
delay(5);
#ifdef USE_MCP2515
setupMCP2515();
#else
setupArduinoCAN();
#endif
keypadConfigure(keypadNewBaudrate);
}

void loop() {
// put your main code here, to run repeatedly:
}

#ifdef USE_MCP2515
void setupMCP2515() {
while (mcp2515.reset() != MCP2515::ERROR_OK) {
Serial.println("MCP reset failed. Retrying...");
delay(100);
}
Serial.println("MCP OK");
mcp2515.setBitrate(mcpBaudrate, mcpClockSpeed);
mcp2515.setNormalMode();
keypadConfigure(keypadBaudrate);
}

void loop() {
// put your main code here, to run repeatedly:
#else
void setupArduinoCAN() {
if (!can.begin(CAN_1000KBPS)) {
Serial.println("CAN initialization failed");
while (1);
}
Serial.println("CAN initialized");
}
#endif

void sendMessage(const struct can_frame &msg) {
#ifdef USE_MCP2515
mcp2515.sendMessage(&msg);
#else
can.send(msg);
#endif
}

void keypadConfigure(uint8_t newBaudrate) {
// This function has delays. Yes it is sloppy. However, the keypad will reboot on some commands and
// time is needed to not blast messages to the transceiver before it can actually send them all out.
// Normally using timers is great for doing this efficiently, but this function SHOULD NOT be called
// often, if ever. It is included more as an ease of use thing to configure your keypad in the way
// the library expects it to be configured so you don't have to manually configure it with a USB-CAN interface.
// YMMV

struct can_frame configurationMsg;

// Send mesage to change CAN protocol to CANOpen if the current protocol is J1939
configurationMsg.can_id = 0x18EF2100 | CAN_EFF_FLAG;
configurationMsg.can_dlc = 8;
configurationMsg.data[0] = 0x04;
configurationMsg.data[1] = 0x1B;
configurationMsg.data[2] = 0x80;
configurationMsg.data[3] = 0x00;
configurationMsg.data[4] = 0xFF;
configurationMsg.data[5] = 0xFF;
configurationMsg.data[6] = 0xFF;
configurationMsg.data[7] = 0xFF;
mcp2515.sendMessage(MCP2515::TXB1, &configurationMsg);
struct can_frame txMsg = {0};
txMsg.can_id = 0x18EF2100 | CAN_EFF_FLAG;
txMsg.can_dlc = 8;
txMsg.data[0] = 0x04;
txMsg.data[1] = 0x1B;
txMsg.data[2] = 0x80;
txMsg.data[3] = 0x00;
txMsg.data[4] = 0xFF;
txMsg.data[5] = 0xFF;
txMsg.data[6] = 0xFF;
txMsg.data[7] = 0xFF;
sendMessage(txMsg);
delay(1000);

configureKeypadNewBaudRate(KEYPAD_NEW_BAUD_RATE);

// send message to configure keypad Baud Rate
configurationMsg.can_id = 0x600 + KEYPAD_CAN_ID;
configurationMsg.can_dlc = 8;
configurationMsg.data[0] = 0x2F;
configurationMsg.data[1] = 0x10;
configurationMsg.data[2] = 0x20;
configurationMsg.data[3] = 0x00;
configurationMsg.data[4] = newBaudrate;
// BYTE 4 configures the baud rate
// 0x00 = 1000k (1M)
// 0x02 = 500k
// 0x03 = 250k
// 0x04 = 125k (default)
configurationMsg.data[5] = 0x00;
configurationMsg.data[6] = 0x00;
configurationMsg.data[7] = 0x00;

mcp2515.setConfigMode();
mcp2515.setBitrate(CAN_125KBPS);
mcp2515.setNormalMode();
mcp2515.sendMessage(&configurationMsg);
delay(1000);

mcp2515.setConfigMode();
mcp2515.setBitrate(CAN_250KBPS);
mcp2515.setNormalMode();
mcp2515.sendMessage(&configurationMsg);
delay(1000);
setKeypadActiveAtStartup(KEYPAD_ACTVIE_AT_STARTUP);
enableCANOpenKeypads(KEYPAD_ACT_CAN_ID);
setStartupLEDShow(KEYPAD_STARTUP_LED_SHOW);
setPeriodicTransmissionTime();
setKeypadModeToPeriodicTransmission();
configureKeypadNewNodeAddress(KEYPAD_NEW_CAN_ID);
}

mcp2515.setConfigMode();
mcp2515.setBitrate(CAN_500KBPS);
mcp2515.setNormalMode();
mcp2515.sendMessage(&configurationMsg);
void configureKeypadNewNodeAddress(uint8_t newCanId)
{
struct can_frame txMsg = {0};
txMsg.can_id = 0x600 + KEYPAD_ACT_CAN_ID;
txMsg.can_dlc = 5;
txMsg.data[0] = 0x2F;
txMsg.data[1] = 0x13;
txMsg.data[2] = 0x20;
txMsg.data[3] = 0x00;
txMsg.data[4] = newCanId;
sendMessage(txMsg);
delay(1000);
}

mcp2515.setConfigMode();
mcp2515.setBitrate(CAN_1000KBPS);
mcp2515.setNormalMode();
mcp2515.sendMessage(&configurationMsg);
void configureKeypadNewBaudRate(uint8_t newBaudrate) {
struct can_frame txMsg = {0};
txMsg.can_id = 0x600 + KEYPAD_ACT_CAN_ID;
txMsg.can_dlc = 5;
txMsg.data[0] = 0x2F;
txMsg.data[1] = 0x10;
txMsg.data[2] = 0x20;
txMsg.data[3] = 0x00;
txMsg.data[4] = newBaudrate;
sendMessage(txMsg);
delay(1000);
}


// set baudrate back to keypad baudrate and continue
mcp2515.setConfigMode();
mcp2515.setBitrate(mcpBaudrate);
mcp2515.setNormalMode();

// Set keypad to be already active at startup
configurationMsg.can_id = 0x600 + KEYPAD_CAN_ID;
configurationMsg.can_dlc = 8;
configurationMsg.data[0] = 0x2F;
configurationMsg.data[1] = 0x12;
configurationMsg.data[2] = 0x20;
configurationMsg.data[3] = 0x00;
// configurationMsg.data[4] = 0x01; //enable
configurationMsg.data[4] = 0x00; // disable
configurationMsg.data[5] = 0x00;
configurationMsg.data[6] = 0x00;
configurationMsg.data[7] = 0x00;
mcp2515.sendMessage(&configurationMsg);
void setKeypadActiveAtStartup(uint8_t StartupActive) {
struct can_frame txMsg = {0};
txMsg.can_id = 0x600 + KEYPAD_ACT_CAN_ID;
txMsg.can_dlc = 5;
txMsg.data[0] = 0x2F;
txMsg.data[1] = 0x12;
txMsg.data[2] = 0x20;
txMsg.data[3] = 0x00;
txMsg.data[4] = StartupActive;
sendMessage(txMsg);
delay(1000);
}

// send message to enable any CANopen keypad on the bus
configurationMsg.can_id = 0x000;
configurationMsg.can_dlc = 8;
configurationMsg.data[0] = 0x01;
configurationMsg.data[1] = 0x00;
configurationMsg.data[2] = 0x00;
configurationMsg.data[3] = 0x00;
configurationMsg.data[4] = 0x00;
configurationMsg.data[5] = 0x00;
configurationMsg.data[6] = 0x00;
configurationMsg.data[7] = 0x00;
mcp2515.sendMessage(&configurationMsg);
void enableCANOpenKeypads(uint8_t id) {
struct can_frame txMsg = {0};
txMsg.can_id = 0x000;
txMsg.can_dlc = 2;
txMsg.data[0] = 0x01;
txMsg.data[1] = id;
sendMessage(txMsg);
delay(1000);
}


// Set startup LED show
configurationMsg.can_id = 0x600 + KEYPAD_CAN_ID;
configurationMsg.can_dlc = 8;
configurationMsg.data[0] = 0x2F;
configurationMsg.data[1] = 0x14;
configurationMsg.data[2] = 0x20;
configurationMsg.data[3] = 0x00;
// configurationMsg.data[4] = 0x00; //disable
// configurationMsg.data[4] = 0x01; //full show
configurationMsg.data[4] = 0x02; // fast flash
configurationMsg.data[5] = 0x00;
configurationMsg.data[6] = 0x00;
configurationMsg.data[7] = 0x00;
mcp2515.sendMessage(&configurationMsg);
void setStartupLEDShow(uint8_t ledShow) {
struct can_frame txMsg = {0};
txMsg.can_id = 0x600 + KEYPAD_ACT_CAN_ID;
txMsg.can_dlc = 5;
txMsg.data[0] = 0x2F;
txMsg.data[1] = 0x14;
txMsg.data[2] = 0x20;
txMsg.data[3] = 0x00;
txMsg.data[4] = ledShow; // fast flash
sendMessage(txMsg);
delay(1000);
}

// Set periodic transmission time
configurationMsg.can_id = 0x600 + KEYPAD_CAN_ID;
configurationMsg.can_dlc = 8;
configurationMsg.data[0] = 0x2B;
configurationMsg.data[1] = 0x00;
configurationMsg.data[2] = 0x18;
configurationMsg.data[3] = 0x05;
configurationMsg.data[4] = 0x28;
configurationMsg.data[5] = 0x01;
configurationMsg.data[6] = 0x00;
configurationMsg.data[7] = 0x00;
mcp2515.sendMessage(&configurationMsg);
void setPeriodicTransmissionTime() {
struct can_frame txMsg = {0};
txMsg.can_id = 0x600 + KEYPAD_ACT_CAN_ID;
txMsg.can_dlc = 6;
txMsg.data[0] = 0x2B;
txMsg.data[1] = 0x00;
txMsg.data[2] = 0x18;
txMsg.data[3] = 0x05;
txMsg.data[4] = 0x28;
txMsg.data[5] = 0x01;
sendMessage(txMsg);
delay(1000);
}

// Set keypad mode to periodic transmission
configurationMsg.can_id = 0x600 + KEYPAD_CAN_ID;
configurationMsg.can_dlc = 8;
configurationMsg.data[0] = 0x2F;
configurationMsg.data[1] = 0x00;
configurationMsg.data[2] = 0x18;
configurationMsg.data[3] = 0x05;
configurationMsg.data[4] = 0x01;
configurationMsg.data[5] = 0x00;
configurationMsg.data[6] = 0x00;
configurationMsg.data[7] = 0x00;
mcp2515.sendMessage(&configurationMsg);
void setKeypadModeToPeriodicTransmission() {
struct can_frame txMsg = {0};
txMsg.can_id = 0x600 + KEYPAD_ACT_CAN_ID;
txMsg.can_dlc = 5;
txMsg.data[0] = 0x2F;
txMsg.data[1] = 0x00;
txMsg.data[2] = 0x18;
txMsg.data[3] = 0x05;
txMsg.data[4] = 0x01;
sendMessage(txMsg);
delay(1000);
}
Loading

0 comments on commit 02ff170

Please sign in to comment.