B-g431-esc1 CAN interface

@Owen_Williams, so, for completeness, here is my github for the CAN bus stuff: GitHub - Igitigit2/SimpleCanLib: CAN bus library for ESP32 and STM32 G431. Discussion here on the forum is fine for me, I guess I am too old for the chatty short lived information flow on Discord - I am always having a hard time to retrive information on Discord again after a short time.

My library is built around a set of MCU specific drivers which are all derived from the same class (class SimpleCan in SimpleCan.h). The drivers have to implement some methods which are virtual in that class. The same class has some methods with default implementations, which form the common layer which is the interface to the application (let’s call it base application layer) and which is the same for all MCUs. Now that I write this, I feel the strong urge, to better seperate the two… Users can either build on my base application layer or the driver layer (which I would not recommend). One important goal for me was that all CAN (user-) application layer stuff can be kept in the same source file, regardless of mixed MCU types in an application. To avoid inconsistencies I did not want to have different (CAN-) sources for e.g. a moter driver, a controller unit, different sensors and a user interface. Another important goal was to have all the application layer stuff free from any hardware interrupt restrictions.

If I compare your and my code, I think you are mostly developing the driver layer (which is also the part that I “stole” from you initially :wink:, while I am a bit more focused on the layer above.

@Grizzly - your assessment is probably correct about working at the driver layer. I’ve somewhat tested my lib against about 6 or 7 boards f407, f405, h7, a couple of g4s and an esp. What I’ve found as a common thread is that getting CAN to work is tough. It doesn’t work until it does! So a main goal is to write something that ‘wants to work’. Also providing a common abstraction (without leaking implementation details) will allow an application layer to be written on top of it. Your profile concept would be an example of that. Perhaps a SimpleFOC profile could be written which is device portable like the rest of SimpleFOC.

Well, my main struggle was with the hardware, those … CAN bus transceivers which stopped working after very few hours, but only after I had made changes to the software as well. It took a while until I suspected the hardware to fail.Since I replaced the chips on the breakout boards, no more issues.

Edit:I never had any issues with the CAN bus on the B-G431B-ESC1. Worked like a charm.

Which transceivers were you using?

Those were the ones which broke after a short time: https://www.amazon.de/gp/product/B081J672SN/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&psc=1

Bought new chips from a German hobbyist dealer and resoldered them.

1 Like

Hi i am trying the same here with the SimpleCan library a BG-431 discovery board and and arduino using MCP2515 i want to use the can interface to get Motorvalues for monitoring so far i tried to follow the instructions in the readme of the simplecan library but i get couple of error messages. apart from that i am an absolute beginner in this field and have never worked with can before any hint is apreciated. did u get the communication between Arduino and the g431 working in the end ?

i want to write a python programm to control the driver and the target value i am using the drivers in angle mode. i want to communicate over serial with the arduino board that then sends commands with the mcp2515 to the drivers to control 6 motors and vise versa want to send motormonitor like target and angle and velocity back to the arduino and over serial to the python programm. anyone did something similiar and get it working i am grateful for any hint.

When i follow the example in the readme file of the SimpleCan Library i get the following errors:

In file included from src\main.cpp:3:
src/test.h:10:7: error: redefinition of ‘class PingPongNotificationsFromCAN’
10 | class PingPongNotificationsFromCAN
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from src\main.cpp:2:
.pio\libdeps\disco_b_g431b_esc1\SimpleCAN - Portable CAN bus library/PingPongCANProfile.h:20:7: note: previous definition of ‘class PingPongNotificationsFromCAN’
20 | class PingPongNotificationsFromCAN
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from src\main.cpp:3:
src/test.h:21:7: error: redefinition of ‘class CANPingPong’
21 | class CANPingPong : public SimpleCANProfile
| ^~~~~~~~~~~
In file included from src\main.cpp:2:
.pio\libdeps\disco_b_g431b_esc1\SimpleCAN - Portable CAN bus library/PingPongCANProfile.h:30:7: note: previous definition of ‘class CANPingPong’
30 | class CANPingPong : public SimpleCANProfile
| ^~~~~~~~~~~
*** [.pio\build\disco_b_g431b_esc1\src\main.cpp.o] Error 1

I will check that later today.

All right, I checked the test program which is in the examples folder of the library and that compiles fine for both MCU types. Could you try that one please? I will check the readme file as well, but since there are only bits and pieces of code, it can well be that it needs updating after my last changes.

I also had a closer look at the readme now and it doesn’t look fundamentally wrong. Looking at the error messages above, it appears that you either included PingPongCanProfile.h twice, or you have the same definitions in your test.h and include test.h plus PingPongCanProfile.h

1 Like

thanks that worked out for me

I managed to send some float values over can Interface and it was working earlier today. Now i get this Can error: CAN Error: Tx buffer overrun

Here is my code:

static uint32_t LastAction=millis();

if ( LastAction+100<millis() )
{
	CANDevice.CANSendFloat(motor.shaft_angle, CANID_PP_FLOAT);
	LastAction=millis();
	
}
else if (CANBroker.ReceivedID==CANID_PP_FLOAT)
{
	float NewVal = CANBroker.ReceivedFloatVal;
	LastAction=millis();
	Serial.println(NewVal);
}

// Make sure we don't react twice to the same message.
CANBroker.ReceivedID = -1;		

CANDevice.Can1->Loop();

Also i want to communicate with an arduino with a mcp 2515 Canshield where the can message in the examplecode looks like this :
canMsg2.can_id = 0x036;
canMsg2.can_dlc = 8;
canMsg2.data[0] = 0x0E;
canMsg2.data[1] = 0x00;
canMsg2.data[2] = 0x00;
canMsg2.data[3] = 0x08;
canMsg2.data[4] = 0x01;
canMsg2.data[5] = 0x00;
canMsg2.data[6] = 0x00;
canMsg2.data[7] = 0xA0;

and also there must be the bitrate set mcp2515.setBitrate(CAN_125KBPS);

i wonder where we setting the bitrate in your library, i couldn´’t find the adequate lines of code in the simplecan Library.

Your received message looks ok, but I did not decode the float value to check if it is correct. In your example above, you either send, or you receive in one loop. I would check for received data and (if required) send in the same loop, so essentially remove the else in there. Also, while testing, it may help to send slower, not every 100ms. Furthermore, the line LastAction=millis(); in the receive path is probably not necessary. If your Tx buffer still shows overrun, then it is likely that you do not properly/fast enough read the data at the other end.

Finally, the place where the data rate is set is in SimpleCan.cpp, line 140. (in the Init() function). I will make it a parameter in the future. The default right now is 1Mbit, so it surprises me that you have to specify 125KBPS for the mcp2515.

1 Like

i managed to send the shaft angle over canbus from one g431 board to another using your library with CANDevice.CANSendFloat(motor.shaft_angle, CANID_PP_FLOAT); Now i would like to send more than only one float over the bus but i don’t really know how to do that. i have tryed following aproach:

on one side i added a new can id to the PingPongCanProfile.h #define CANID_PP_FLOAT_current 5 further more i added
case CANID_PP_FLOAT_current:
Val = CANGetFloat(rxData);
pRxCommands->ReceivedFloat_current(Val);
break;
in the HandleCanMessage function of CanPingPong class.

Then i added void ReceivedFloat_current(const float Val)
{
Serial.printf(“Rcvd float current: %.3f\n”, Val);
ReceivedFloatVal_current = Val;
ReceivedID = CANID_PP_FLOAT_current;
};
in RxFromCAN class and added the following in the main loop

else if (CANBroker.ReceivedID==CANID_PP_FLOAT_current)
{
float NewVal = CANBroker.ReceivedFloatVal_current;

Serial.println(NewVal); 

and i added
CANDevice.CANSendFloat(motor.shaft_angle, CANID_PP_FLOAT);
CANDevice.CANSendFloat(motor.current.d, CANID_PP_FLOAT_current);
LastAction=millis();
on the other side (meaning: the other board which is sending)

i am not receiving the values of motor current only the angle values. what am i missing doing wrong ? i must admit that i do not fully understand your library hence is hard for me to change code acordingly. Thanks for any hints and advices

Hm, this all looks pretty much ok. Assuming you really updated both MCUs, I don’t see from the code snippets what’s wrong. If you send me your .cpp/.h files, I can have a look at them. However, this is now getting quite off-topic for SimpleFOC, so maybe we should take it offline. We can discuss it either as DM here, or on my github page for SimpleCanLib.

I am just modifying my CAN library and encounter the problem that I need to convert Arduino pin definitions (e.g. PA11) to a STM32 GPIO_InitTypeDef HAL structure which is passed to HAL_GPIO_Init(). Converting the pin number seems quite straight forward, but the port is not so clear. I came up with the following conversion routine:

void ArduinoPinToHAL(uint32_t APin, uint32_t* HALPin, GPIO_TypeDef** HALPort)
{
	PinName Pin = digitalPinToPinName(APin);
	uint32_t HPin  = STM_LL_GPIO_PIN(APin);

	uint32_t HPort = STM_PORT(APin);
	GPIO_TypeDef* PortBase=0;
	switch(HPort)
	{
		case 0: PortBase = (GPIO_TypeDef*)GPIOA_BASE; break;
		case 1: PortBase = (GPIO_TypeDef*)GPIOB_BASE; break;
		case 2: PortBase = (GPIO_TypeDef*)GPIOC_BASE; break;
		case 3: PortBase = (GPIO_TypeDef*)GPIOD_BASE; break;
		case 4: PortBase = (GPIO_TypeDef*)GPIOE_BASE; break;
		case 5: PortBase = (GPIO_TypeDef*)GPIOF_BASE; break;
		case 6: PortBase = (GPIO_TypeDef*)GPIOG_BASE; break;
		default: Serial.println("ArduinoPin2HAL(): Error, invalid port number!");
	}

	*HALPin = HPin;
	*HALPort = PortBase;
}

which shall then be called like this:

uint32_t Pin;
GPIO_TypeDef*    Port;
GPIO_InitTypeDef GPIO_InitStruct = { 0 };

ArduinoPinToHAL(PA11, &Pin, &Port);
GPIO_InitStruct.Pin = Pin;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF9_FDCAN1;
HAL_GPIO_Init(Port, &GPIO_InitStruct);

Is this a safe way or is there a better way? I don’t believe I am the first one to encounter that problem and expect there to be an already existing standard method, but haven’t found it yet.

Hey,

Have you looked into using the function pinmap_pinout()?

PinName _pinRX = digitalPinToPinName(pinRX);
PinName _pinTX = digitalPinToPinName(pinTX);
pinmap_pinout(_pinRX, PinMap_CAN_RD);
pinmap_pinout(_pinTX, PinMap_CAN_TD);

Also there is a function pinmap_peripheral(), which for the timers I use like this:

TIM_TypeDef *InstanceA = (TIM_TypeDef *)pinmap_peripheral(_pinA, PinMap_TIM);

But I am not sure if it works for CAN or what the return-type would be…

I think, I figured it out. What digitalWrite() does is equivalent to the following (as far as the pins go):


void ArduinoPinToHAL(uint32_t APin, uint32_t* HALPin, GPIO_TypeDef** HALPort)
{
	uint32_t Pin = digitalPinToPinName(APin);
	*HALPort = get_GPIO_Port(STM_PORT(Pin));
	*HALPin  = STM_LL_GPIO_PIN(Pin);
}

It is terribly confusing.