Hello,
First post, lots of PLC programming experience, new to Arduino / C++.
I'm building an air suspension controller for a vehicle. One Adafruit Feather M4 CAN Express controls the suspension and communicates with a 2nd unit via CAN bus which is a user interface on the dash. I have the electronics working. and CAN bus talking.
The data being sent is in an eight byte array.
I need one byte to represent eight bools and the remaining seven bytes will be int values.
I want to give easy names to all of the data, so I used #define to reference bytes in the array, which works as expected.
I'd like to do the same with the eight bits (bit 0 = pump_on, bit2 = pump_fail, etc...) but can figure out a way to name the individual bits.
Any ideas?
I tried sending a structure, but couldnt get it to work.
Thank you.
// BODY MODULE (blue light)
#include <CAN.h>
#include <Adafruit_NeoPixel.h>
Adafruit_NeoPixel strip(1, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800);
#define MY_PACKET_ID 0xAD
uint32_t timestamp;
//initilaize arrays for inbound and outbound data
struct outstruct { //tried sending a structure, no luck so far
bool bit0;
bool bit1;
bool bit2;
bool bit3;
bool bit4;
bool bit5;
bool bit6;
bool bit7;
int int0;
int int1;
int int2;
int int3;
int int4;
int int5;
int int6;
};
byte sendbuff[8];
byte rcvbuff[8];
void setup() {
Serial.begin(9600);
pinMode(PIN_CAN_STANDBY, OUTPUT);
digitalWrite(PIN_CAN_STANDBY, false); // turn off STANDBY
pinMode(PIN_CAN_BOOSTEN, OUTPUT);
digitalWrite(PIN_CAN_BOOSTEN, true); // turn on booster
//Light the neopixel blue so we can identify the BODY MODULE on the testing bench
strip.begin();
strip.setBrightness(5);
strip.setPixelColor(0, 0, 0, 255); //set color to green
strip.show();
//define easy names for data
#define TANKPRES sendbuff[0]
#define LEFTPRES sendbuff[1]
#define RIGHTPRES sendbuff[2]
// send some test values.....
TANKPRES = 55;
LEFTPRES = 66;
RIGHTPRES = 77;
// start the CAN bus at 250 kbps
if (!CAN.begin(250000)) {
Serial.println("Starting CAN failed!");
while (1);
}
timestamp = millis();
}
void loop() {
// every 1000 ms send out a packet
if ((millis() - timestamp) > 1000) {
Serial.print("Sending packet");
CAN.beginPacket(MY_PACKET_ID);
CAN.write(sendbuff, 8);
CAN.endPacket();
Serial.print(sendbuff[0]);
Serial.print(" ");
Serial.print(sendbuff[1]);
Serial.print(" ");
Serial.print(sendbuff[2]);
Serial.print(" ");
Serial.print(sendbuff[3]);
Serial.print(" ");
Serial.print(sendbuff[4]);
Serial.print(" ");
Serial.print(sendbuff[5]);
Serial.print(" ");
Serial.print(sendbuff[6]);
Serial.print(" ");
Serial.print(sendbuff[7]);
Serial.println();
timestamp = millis();
}
I did.
I (think that I) understand how to set and read bits. Once I start writing the bulk of my code, I want to be able to do something like PUMPON = 1 instead of bitset(sendbuff[0] , 2). (assuming bit #3 of the first bite represents the pump running state)
I'm just looking for a plain English way to reference the bits, like I'm doing with the #define on the bytes.
#define CLR(x,y) (x&=(~(1<<y))) //bit y of x will 0
#define SET(x,y) (x|=(1<<y)) //1
#define CHK(x,y) (x & (1<<y)) // gives a value of bit y
#define TOG(x,y) (x^=(1<<y)) //will toggle
// BODY MODULE (blue light)
#define PIN_NEOPIXEL 2
#define PIN_CAN_STANDBY 3
#define PIN_CAN_BOOSTEN 4
#include <CAN.h>
#include <Adafruit_NeoPixel.h>
Adafruit_NeoPixel strip(1, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800);
#define MY_PACKET_ID 0xAD
#define TANKPRES sendbuff[0] //define easy names for data
#define LEFTPRES sendbuff[1]
#define RIGHTPRES sendbuff[2]
#define CLR(x,y) (x&=(~(1<<y))) //bit y of x will 0
#define SET(x,y) (x|=(1<<y)) //1
#define CHK(x,y) (x & (1<<y)) // gives a value of bit y
#define TOG(x,y) (x^=(1<<y)) //will toggle
uint32_t Timestamp;
const uint16_t Period=1000UL;
byte sendbuff[8];
byte rcvbuff[8];
void setup() {
Serial.begin(9600);
pinMode(PIN_CAN_STANDBY, OUTPUT);
digitalWrite(PIN_CAN_STANDBY, false); // turn off STANDBY
pinMode(PIN_CAN_BOOSTEN, OUTPUT);
digitalWrite(PIN_CAN_BOOSTEN, true); // turn on booster
//Light the neopixel blue so we can identify the BODY MODULE on the testing bench
strip.begin();
strip.setBrightness(5);
strip.setPixelColor(0, 0, 0, 255); //set color to green
strip.show();
// send some test values.....
TANKPRES = 55;
LEFTPRES = 66;
RIGHTPRES = 77;
if (!CAN.begin(250000)) {// start the CAN bus at 250 kbps
Serial.println("Starting CAN failed!");
while (1);
}
Timestamp = millis();
}
void loop() {
// every 1000 ms send out a packet
if ((millis() - Timestamp) > Period) {
Serial.print("Sending packet");
CAN.beginPacket(MY_PACKET_ID);
CAN.write(sendbuff, 8);
CAN.endPacket();
for (int8_t i = 0; i < 8; i++) {
Serial.print(sendbuff[i]);
if (i % 7)Serial.print(" ");
else Serial.println();
}
Timestamp += Period;
}
The last time I looked a byte was 8 bits and an int was 16 bits in Arduino language, so what magic will be used to accomplish this. CAN is bytes, 0-7 what they are is entirely up to your software. Did you bytes instead of int?
You should endeavor to get that working. Structs provide bitfield capability which is exactly what you're asking for. Generally speaking, pre-processor macros should used only as a last resort.
Hi,
I think I follow most of this, however the code won't compile with this line: CAN.write(&data, sizeof(data));
If I comment out the one line it compiles.
The thrown error is:
D:\..some file path...\Body_Module_02.ino: In function 'void loop()':
D:\..some file path...\Body_Module_02.ino:94:34: error: no matching function for call to 'CANSAME5x::write(ControllerData*, unsigned int)'
94 | CAN.write(&data, sizeof(data));
| ^
In file included from c:\Users\..some file path...\libraries\CAN_Adafruit_Fork\src/CANSAME5x.h:5,
from c:\Users\..some file path...\libraries\CAN_Adafruit_Fork\src/CAN.h:8,
from D:\..some file path...\Body_Module_02.ino:3:
c:\Users\..some file path...\libraries\CAN_Adafruit_Fork\src/CANController.h:26:18: note: candidate: 'virtual size_t CANControllerClass::write(uint8_t)'
26 | virtual size_t write(uint8_t byte);
| ^~~~~
c:\Users\..some file path...\libraries\CAN_Adafruit_Fork\src/CANController.h:26:18: note: candidate expects 1 argument, 2 provided
c:\Users\..some file path...\libraries\CAN_Adafruit_Fork\src/CANController.h:27:18: note: candidate: 'virtual size_t CANControllerClass::write(const uint8_t*, size_t)'
27 | virtual size_t write(const uint8_t *buffer, size_t size);
| ^~~~~
c:\Users\..some file path...\libraries\CAN_Adafruit_Fork\src/CANController.h:27:39: note: no known conversion for argument 1 from 'ControllerData*' to 'const uint8_t*' {aka 'const unsigned char*'}
27 | virtual size_t write(const uint8_t *buffer, size_t size);
| ~~~~~~~~~~~~~~~^~~~~~
exit status 1
Compilation error: no matching function for call to 'CANSAME5x::write(ControllerData*, unsigned int)'
// Copyright (c) Sandeep Mistry. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef CAN_H
#define CAN_H
#if defined(ADAFRUIT_FEATHER_M4_CAN)
#include "CANSAME5x.h"
#elif defined(ARDUINO_ARCH_ESP32)
#include "ESP32SJA1000.h"
#else
#include "MCP2515.h"
#endif
#endif
I assume we are assigning (defining?) the struct ControllerData to data? But why?
I tried looking it up but don't know what terminology to use. I tried Alias, etc.
I see that from this point on the struct is referenced as "data", i.e., data.pumpOn, etc.