-
-
Notifications
You must be signed in to change notification settings - Fork 8.3k
ESP32: CAN(TWAI) driver #7381
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ESP32: CAN(TWAI) driver #7381
Conversation
WIP: Code is tested on ESP32 based TTGO-T-display and works with CANopen inclinometer. |
there is a problem in machine_can.c in this code block switch ((int)args[ARG_baudrate].u_int) {
case 0:
timing = &((twai_timing_config_t) {
.brp = args[ARG_prescaler].u_int,
.sjw = args[ARG_sjw].u_int,
.tseg_1 = args[ARG_bs1].u_int,
.tseg_2 = args[ARG_bs1].u_int,
.triple_sampling = false
});
break; it should read switch ((int)args[ARG_baudrate].u_int) {
case 0:
timing = &((twai_timing_config_t) {
.brp = args[ARG_prescaler].u_int,
.sjw = args[ARG_sjw].u_int,
.tseg_1 = args[ARG_bs1].u_int,
.tseg_2 = args[ARG_bs2].u_int,
.triple_sampling = false
});
break; ARG_bs1 was used for both tseg_1 and tseg_2 I can see from the source that there are pre defined timings for specific bitrates. I have written a helper class in Python that deals with calculating the proper timings for any bitrate for a number of CAN interfaces. These are the things needed to calculate the proper timing registers for the TWAI interface in the ESP32 fclock = 40000000 If the chip is a version 2 or above chip it will support IDK if you have support for all of the preset timing macros that are available. TWAI_TIMING_CONFIG_1MBITS() version 2+ has these additional macros TWAI_TIMING_CONFIG_20KBITS() The nice thing about the helper class I wrote is it will find the closest matching timing for the bitrate supplied CAN networks have a bit of flexibility with the bitrates and a device doesn't have to have an exact bitrate to work. |
@kdschlosser Thank you very mush, keen eyes.
@kdschlosser Would you post the helper class in Python that deals with calculating the proper timings for any bitrate for a number of CAN interfaces? |
Yeah I can post that for ya. Let me add in the ESP TWAI values to it and key up an example for use. I had a visit to the the dentist yesterday so I am pretty well medicated and I am running in slow gear so it might take me a day or 2 to get it over to ya. I really need to have the CAN functionality of the ESP working instead of having to use an SPI interface. I need to have one extra pin available. My only question for making the modifications is knowing what version of the ESP that is being used version 1 or version 2+ is there a way to collect that information from micropython? |
just run the attached script and it will spit out Bitrate class instances that will work. I used a bitrate of 33000 as the value I fed in with a calculated tolerance of 0.1% The program I wrote handles interfaces based on the following chipsets
I have not hammered out whether or not the chip version for the ESP is 1 or 2. Once I get that sorted out I will be able to add in the code to extend the variables to offer a larger number of bitrate matches. There is an error rate that the user is able to plug in that will spit out bitrate matches that are +- that error rate. an error rate of 0.0 will mean only identical matches are allowed. So with the ESP32 if I set the error rate to 0.0 there are no matches. the closest bitrate match available for the esp32 is 33003 for the 33003 bitrate provided as a result there are the values given.
I wrote the code so the user does not need to have any knowledge of the CAN interface being used. The only things they would need to pass into the constructor of the interface is either an integer bitrate or a Bitrate class instance. From there the constructor can turn an interger bitrate into the Bitrate instance or use the supplied bitrate instance. To get the closest possible match the constructor would call the I am still fuzzy on the use of the sjw value (1-4 typ) parameter. I believe it's use is to compensate but the bitrate error. the bitrate error is the difference between the target bitrate and the bitrate the interface is actually able to do. The sjw makes 2 different bitrates able to work with one another.. Up to a point! I have not finagled with it enough to be able to determine what a proper sjw would be based on the bitrate difference. maybe someone might be able to help out. I am sure that my calculations are probably not perfect and could could use some tweaking. I know that this does work with the sja1000 based interfaces using brp, btr0 and btr1 to fill the timing registers for that interface. It appears like the ESP32 TWAI also uses the same registers. I would need to know what the output is from the defined macros in the ESP32 API in order to ensure everything is as it should be. I do know that the calculations would run a lot faster if the code was converted to C code I am not sure how it could remain flexible so if a user wanted to use an interface other then the one built into the ESP32 they will still have use of this helper. I have found that the single hardest thing about using any CAN interface attached to a micro controller is the bit timings. My specific use case is for use in a vehicle. Vehicles have more then one CAN bus in them. In my car there are 4 buses and each of them runs at a different speed. I would need to have 4 interfaces to communicate with all 4 of them. In this kind of a scenario to minimize the number of pins being used on an ESP32 which would be 2 pins for each interface, I would use less pins if I used 4 SPI interfaces which would total 3 pins for SPI and 4 pins for CS. it would only save a single pin but that one pin can make a difference. In my use case I am short by 1 pin and I am only running a single external interface. If I can use the internal TWAI interface for CAN 4 pins oud no longer be used for SPI (with CS) that would reduce the number of pins for an external interface from 4 down to 2 (CAN +, CAN -) thus freeing up 2 pins and giving me the one additional pin that is needed. Here is the script. It does have an example that runs using the ESP32 values. I have not tested if on Micropython and there could be changes needed to be made to have it run. I have also not optimized the code, this is a basic "proof of concept" and I know I could reduce the memory footprint and also increase the speed. There can also be errors in some of the calculations that need to be corrected, I have only tested it with sja1000 |
Here is an updated version of the script. I added in a sample point tolerance when enumerating available timings. I also added in handling of version 1 or version 2 chips of the ESP32. which I do not know how to know what version of the chip is being used once that is added to it I can easily bring that into the script so it will calculate the correct timings. It appears that a version 2 chip is able to handle bitrates lower then 10000. It also appears as tho there are 3 variations of the ESP32 that need to be accounted for.
It also appears that the f_sys or clock used by the TWAI controller is 80mHz and not they 40mHz crystal speed.
|
I am not sure if there is a way to identify the clock being used. So I have defaulted to 80000000. All of these values can easily be changed by a user if necessary. |
I know that is this trivial
CAN networks are all about 1's and 0's and nothing more, so bitrate is the more appropriate vernacular to use. again I know it's trivial, but the engineering side of me would wonder how many bits are in a baud because a baud is not a representation of a bit it can be a representation of several bits grouped together. |
Another python based program I wrote reads and writes Vector CANdb++ files (.dbc) files. This program takes an incoming CAN frame and decodes the frame an presents it to a user in a manner that makes it easy to interact with. It will also encode frames making it easier for a user. The user would not have to have any knowledge of CAN networking or frames and how the frames are supposed to be formatted. Here is a DBC file that I wrote that decodes OBDII compliant data and it also builds a request packet that can be sent out to the network. The program I wrote has 4543 lines of code and will handle decoding and encoding messages that have been defined inside of a file like the one I attached above. It can decode 443 packets (incoming messages) and encode 447 packets (outgoing messages). if the packet contains multiple pieces of information it breaks them apart and will run the necessary equations on the data to decode it into something a user will understand along with whatever unit of measure they want to view the data in. A single chip having the capabilities of doing this would be enormously valuable. The ELM based chips are garbage, they are to slow an 1/2 the time don't work properly. IDK if the ESP TWAI can be run in multiples. From the ESP API docs it looks like it is able to. If it can have multiple instances assigned to different pins I could use a single ESP 32 and have it connected to all 4 of the CAN networks in my car at the same time. I have not come across an interface that is able to attach to more then 2 networks at the same time. and the ones that can connect to 2 networks get expensive. If the ability for a user to drop in a DBC file for their vehicle and then be able to interact with the car easily would be awesome. I know that kind of functionality would require using a WROVER-B ESP32 it would be something really neat to be able to do. |
I have a question. I have been digging about the SDK for the TWAI portion of the ESP32. It mentions having to use an transceiver like SN65HVD23X.. Is this something that is necessary?? and what would be the purpose to running a transceiver if the ESP32 has the capabilities to handle the CAN protocol why would it mention needing to use a transceiver? Another thing I would like to know if there is a way to access the TWAI functions by use of ctypes. I do this quite often when needing to use a function that is inside of a shared library in a posix system or a Windows system. so long as the function is an exported function it can be accessed from Python code instead of having to write a driver in C code. I do understand that C code is faster but this is not something I would think would provide any kind of a speed boost and because of the type conversions done in C when needing to pass an array and thus filling the array it appears it is making it overly complex. All of the TWAI objects I have seen thus far are enumerations, structures and unions that can easily be written in python. If there is a way to load the shared library containing the TWAI functions I can have a CAN driver written in < an hour. Even if the functions aren't available in a manner that would make them easy to get to using ctypes maybe making a driver that simply exposes the functions and putting together the data structures that are needing to be passed to those functions can be done in Python code. Here is all of the macros, structures, enumerations and data types that TWAI uses. the only things that are missing are the functions. click to expand codeimport ctypes
ENUM = ctypes.c_int
uint32_t = ctypes.c_uint32
uint8_t = ctypes.c_uint8
c_bool = ctypes.c_bool
c_int = ctypes.c_int
# Alert(1): No more messages to transmit
TWAI_ALERT_TX_IDLE = 0x00000001
# Alert(2): The previous transmission was successful
TWAI_ALERT_TX_SUCCESS = 0x00000002
# Alert(4): Both error counters have dropped below error warning limit
TWAI_ALERT_BELOW_ERR_WARN = 0x00000004
# Alert(8): TWAI controller has become error active
TWAI_ALERT_ERR_ACTIVE = 0x00000008
# Alert(16): TWAI controller is undergoing bus recovery
TWAI_ALERT_RECOVERY_IN_PROGRESS = 0x00000010
# Alert(32): TWAI controller has successfully completed bus recovery
TWAI_ALERT_BUS_RECOVERED = 0x00000020
# Alert(64): The previous transmission lost arbitration
TWAI_ALERT_ARB_LOST = 0x00000040
# Alert(128): One of the error counters have exceeded the error warning limit
TWAI_ALERT_ABOVE_ERR_WARN = 0x00000080
# Alert(256): A (Bit, Stuff, CRC, Form, ACK) error has occurred on the bus
TWAI_ALERT_BUS_ERROR = 0x00000100
# Alert(512): The previous transmission has failed (for single shot transmission)
TWAI_ALERT_TX_FAILED = 0x00000200
# Alert(1024): The RX queue is full causing a frame to be lost
TWAI_ALERT_RX_QUEUE_FULL = 0x00000400
# Alert(2048): TWAI controller has become error passive
TWAI_ALERT_ERR_PASS = 0x00000800
# Alert(4096): Bus-off condition occurred. TWAI controller can no longer influence bus
TWAI_ALERT_BUS_OFF = 0x00001000
# Alert(8192): An RX FIFO overrun has occurred
TWAI_ALERT_RX_FIFO_OVERRUN = 0x00002000
# Alert(16384): An message transmission was cancelled and retried due to an errata workaround
TWAI_ALERT_TX_RETRIED = 0x00004000
# Alert(32768): The TWAI controller was reset
TWAI_ALERT_PERIPH_RESET = 0x00008000
# Bit mask to enable all alerts during configuration
TWAI_ALERT_ALL = 0x0000FFFF
# Bit mask to disable all alerts during configuration
TWAI_ALERT_NONE = 0x00000000
# Bit mask to enable alerts to also be logged when they occur. Note that logging from the ISR is disabled
# if CONFIG_TWAI_ISR_IN_IRAM is enabled (see docs).
TWAI_ALERT_AND_LOG = 0x00010000
# Marks GPIO as unused in TWAI configuration
TWAI_IO_UNUSED = -1
# Accept a Level 1 interrupt vector (lowest priority)
ESP_INTR_FLAG_LEVEL1 = 1 << 1
# Accept a Level 2 interrupt vector
ESP_INTR_FLAG_LEVEL2 = 1 << 2
# Accept a Level 3 interrupt vector
ESP_INTR_FLAG_LEVEL3 = 1 << 3
# Accept a Level 4 interrupt vector
ESP_INTR_FLAG_LEVEL4 = 1 << 4
# Accept a Level 5 interrupt vector
ESP_INTR_FLAG_LEVEL5 = 1 << 5
# Accept a Level 6 interrupt vector
ESP_INTR_FLAG_LEVEL6 = 1 << 6
# Accept a Level 7 interrupt vector (highest priority)
ESP_INTR_FLAG_NMI = 1 << 7
# Interrupt can be shared between ISRs
ESP_INTR_FLAG_SHARED = 1 << 8
# Edge-triggered interrupt
ESP_INTR_FLAG_EDGE = 1 << 9
# ISR can be called if cache is disabled
ESP_INTR_FLAG_IRAM = 1 << 10
# Return with this interrupt disabled
ESP_INTR_FLAG_INTRDISABLED = 1 << 11
# Low and medium prio interrupts. These can be handled in C.
ESP_INTR_FLAG_LOWMED = (
ESP_INTR_FLAG_LEVEL1 |
ESP_INTR_FLAG_LEVEL2 |
ESP_INTR_FLAG_LEVEL3
)
# High level interrupts. Need to be handled in assembly.
ESP_INTR_FLAG_HIGH = (
ESP_INTR_FLAG_LEVEL4 |
ESP_INTR_FLAG_LEVEL5 |
ESP_INTR_FLAG_LEVEL6 |
ESP_INTR_FLAG_NMI
)
# Mask for all level flags
ESP_INTR_FLAG_LEVELMASK = ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_HIGH
def TWAI_GENERAL_CONFIG_DEFAULT(tx_io_num, rx_io_num, op_mode):
res = twai_general_config_t()
res.mode = op_mode
res.tx_io = tx_io_num
res.rx_io = rx_io_num
res.clkout_io = TWAI_IO_UNUSED
res.bus_off_io = TWAI_IO_UNUSED
res.tx_queue_len = 5
res.rx_queue_len = 5
res.alerts_enabled = TWAI_ALERT_NONE
res.clkout_divider = 0
res.intr_flags = ESP_INTR_FLAG_LEVEL1
return res
class gpio_num_t(ENUM):
# Use to signal not connected to S/W
GPIO_NUM_NC = -1
# GPIO0, input and output
GPIO_NUM_0 = 0
# GPIO1, input and output
GPIO_NUM_1 = 1
# GPIO2, input and output
GPIO_NUM_2 = 2
# GPIO3, input and output
GPIO_NUM_3 = 3
# GPIO4, input and output
GPIO_NUM_4 = 4
# GPIO5, input and output
GPIO_NUM_5 = 5
# GPIO6, input and output
GPIO_NUM_6 = 6
# GPIO7, input and output
GPIO_NUM_7 = 7
# GPIO8, input and output
GPIO_NUM_8 = 8
# GPIO9, input and output
GPIO_NUM_9 = 9
# GPIO10, input and output
GPIO_NUM_10 = 10
# GPIO11, input and output
GPIO_NUM_11 = 11
# GPIO12, input and output
GPIO_NUM_12 = 12
# GPIO13, input and output
GPIO_NUM_13 = 13
# GPIO14, input and output
GPIO_NUM_14 = 14
# GPIO15, input and output
GPIO_NUM_15 = 15
# GPIO16, input and output
GPIO_NUM_16 = 16
# GPIO17, input and output
GPIO_NUM_17 = 17
# GPIO18, input and output
GPIO_NUM_18 = 18
# GPIO19, input and output
GPIO_NUM_19 = 19
# GPIO20, input and output
GPIO_NUM_20 = 20
# GPIO21, input and output
GPIO_NUM_21 = 21
# GPIO22, input and output
GPIO_NUM_22 = 22
# GPIO23, input and output
GPIO_NUM_23 = 23
# GPIO25, input and output
GPIO_NUM_25 = 25
# GPIO26, input and output
GPIO_NUM_26 = 26
# GPIO27, input and output
GPIO_NUM_27 = 27
# GPIO28, input and output
GPIO_NUM_28 = 28
# GPIO29, input and output
GPIO_NUM_29 = 29
# GPIO30, input and output
GPIO_NUM_30 = 30
# GPIO31, input and output
GPIO_NUM_31 = 31
# GPIO32, input and output
GPIO_NUM_32 = 32
# GPIO33, input and output
GPIO_NUM_33 = 33
# GPIO34, input mode only
GPIO_NUM_34 = 34
# GPIO35, input mode only
GPIO_NUM_35 = 35
# GPIO36, input mode only
GPIO_NUM_36 = 36
# GPIO37, input mode only
GPIO_NUM_37 = 37
# GPIO38, input mode only
GPIO_NUM_38 = 38
# GPIO39, input mode only
GPIO_NUM_39 = 39
GPIO_NUM_MAX = 40
TWAI_EXTD_ID_MASK = 0x1FFFFFFF # Bit mask for 29 bit Extended Frame Format ID
TWAI_STD_ID_MASK = 0x7FF # Bit mask for 11 bit Standard Frame Format ID
TWAI_FRAME_MAX_DLC = 8 # Max data bytes allowed in TWAI
TWAI_FRAME_EXTD_ID_LEN_BYTES = 4 # EFF ID requires 4 bytes (29bit)
TWAI_FRAME_STD_ID_LEN_BYTES = 2 # SFF ID requires 2 bytes (11bit)
TWAI_ERR_PASS_THRESH = 128 # Error counter threshold for error passive
TWAI_MSG_FLAG_NONE = 0x00 # No message flags (Standard Frame Format)
TWAI_MSG_FLAG_EXTD = 0x01 # Extended Frame Format (29bit ID)
TWAI_MSG_FLAG_RTR = 0x02 # Message is a Remote Frame
TWAI_MSG_FLAG_SS = 0x04 # Transmit as a Single Shot Transmission. Unused for received.
TWAI_MSG_FLAG_SELF = 0x08 # Transmit as a Self Reception Request. Unused for received.
TWAI_MSG_FLAG_DLC_NON_COMP = 0x10 # Message's Data length code is larger than 8. This will break compliance with TWAI
class twai_message_t(ctypes.Structure):
class _union(ctypes.Union):
class _struct(ctypes.Structure):
_fields_ = [
# The order of these bits must match deprecated message flags for compatibility reasons
# Extended Frame Format (29bit ID)
('extd', uint32_t, 1),
# Message is a Remote Frame
('rtr', uint32_t, 1),
# Transmit as a Single Shot Transmission. Unused for received.
('ss', uint32_t, 1),
# Transmit as a Self Reception Request. Unused for received.
('self', uint32_t, 1),
# Message's Data length code is larger than 8. This will break compliance with ISO 11898-1
('dlc_non_comp', uint32_t, 1),
# Reserved bits
('reserved', uint32_t, 27)
]
_anonymous_ = ('_struct',)
_fields_ = [
('_struct', _struct),
# Todo: Deprecate flags
# < Deprecated: Alternate way to set bits using message flags
('flags', uint32_t)
]
_anonymous_ = ('_union',)
_fields_ = [
('_union', _union),
# 11 or 29 bit identifier
('identifier', uint32_t),
# Data length code
('data_length_code', uint8_t),
# Data bytes (not relevant in RTR frame)
('data', uint8_t * TWAI_FRAME_MAX_DLC)
]
class twai_timing_config_t(ctypes.Structure):
_fields_ = [
# Baudrate prescaler (i.e., APB clock divider). Any even number from 2 to 128 for ESP32, 2 to 32768 for ESP32S2.
# For ESP32 Rev 2 or later, multiples of 4 from 132 to 256 are also supported
('brp', uint32_t),
# Timing segment 1 (Number of time quanta, between 1 to 16)
('tseg_1', uint8_t),
# Timing segment 2 (Number of time quanta, 1 to 8)
('tseg_2', uint8_t),
# Synchronization Jump Width (Max time quanta jump for synchronize from 1 to 4)
('sjw', uint8_t),
# Enables triple sampling when the TWAI controller samples a bit
('triple_sampling', c_bool)
]
def TWAI_TIMING_CONFIG_1KBITS():
res = twai_timing_config_t()
res.brp = 4000
res.tseg_1 = 15
res.tseg_2 = 8
res.sjw = 3
res.triple_sampling = False
return res
def TWAI_TIMING_CONFIG_5KBITS():
res = twai_timing_config_t()
res.brp = 800
res.tseg_1 = 15
res.tseg_2 = 8
res.sjw = 3
res.triple_sampling = False
return res
def TWAI_TIMING_CONFIG_10KBITS():
res = twai_timing_config_t()
res.brp = 400
res.tseg_1 = 15
res.tseg_2 = 8
res.sjw = 3
res.triple_sampling = False
return res
def TWAI_TIMING_CONFIG_12_5KBITS():
res = twai_timing_config_t()
res.brp = 256
res.tseg_1 = 16
res.tseg_2 = 8
res.sjw = 3
res.triple_sampling = False
return res
def TWAI_TIMING_CONFIG_16KBITS():
res = twai_timing_config_t()
res.brp = 200
res.tseg_1 = 16
res.tseg_2 = 8
res.sjw = 3
res.triple_sampling = False
return res
def TWAI_TIMING_CONFIG_20KBITS():
res = twai_timing_config_t()
res.brp = 200
res.tseg_1 = 15
res.tseg_2 = 4
res.sjw = 3
res.triple_sampling = False
return res
def TWAI_TIMING_CONFIG_25KBITS():
res = twai_timing_config_t()
res.brp = 128
res.tseg_1 = 16
res.tseg_2 = 8
res.sjw = 3
res.triple_sampling = False
return res
def TWAI_TIMING_CONFIG_50KBITS():
res = twai_timing_config_t()
res.brp = 80
res.tseg_1 = 15
res.tseg_2 = 4
res.sjw = 3
res.triple_sampling = False
return res
def TWAI_TIMING_CONFIG_100KBITS():
res = twai_timing_config_t()
res.brp = 40
res.tseg_1 = 15
res.tseg_2 = 4
res.sjw = 3
res.triple_sampling = False
return res
def TWAI_TIMING_CONFIG_125KBITS():
res = twai_timing_config_t()
res.brp = 32
res.tseg_1 = 15
res.tseg_2 = 4
res.sjw = 3
res.triple_sampling = False
return res
def TWAI_TIMING_CONFIG_250KBITS():
res = twai_timing_config_t()
res.brp = 16
res.tseg_1 = 15
res.tseg_2 = 4
res.sjw = 3
res.triple_sampling = False
return res
def TWAI_TIMING_CONFIG_500KBITS():
res = twai_timing_config_t()
res.brp = 8
res.tseg_1 = 15
res.tseg_2 = 4
res.sjw = 3
res.triple_sampling = False
return res
def TWAI_TIMING_CONFIG_800KBITS():
res = twai_timing_config_t()
res.brp = 4
res.tseg_1 = 16
res.tseg_2 = 8
res.sjw = 3
res.triple_sampling = False
return res
def TWAI_TIMING_CONFIG_1MBITS():
res = twai_timing_config_t()
res.brp = 4
res.tseg_1 = 15
res.tseg_2 = 4
res.sjw = 3
res.triple_sampling = False
return res
class twai_filter_config_t(ctypes.Structure):
_fields_ = [
# 32-bit acceptance code
('acceptance_code', uint32_t),
# 32-bit acceptance mask
('acceptance_mask', uint32_t),
# Use Single Filter Mode (see documentation)
('single_filter', c_bool)
]
def TWAI_FILTER_CONFIG_ACCEPT_ALL():
res = twai_filter_config_t()
res.acceptance_code = 0
res.acceptance_mask = 0xFFFFFFFF
res.single_filter = True
class twai_state_t(ENUM):
TWAI_STATE_STOPPED = 0
TWAI_STATE_RUNNING = 1
TWAI_STATE_BUS_OFF = 2
TWAI_STATE_RECOVERING = 3
class twai_mode_t(ENUM):
"""TWAI Controller operating modes."""
# Normal operating mode where TWAI controller can send/receive/acknowledge messages
TWAI_MODE_NORMAL = 0
# Transmission does not require acknowledgment. Use this mode for self testing
TWAI_MODE_NO_ACK = 1
# The TWAI controller will not influence the bus (No transmissions or acknowledgments) but can receive messages
TWAI_MODE_LISTEN_ONLY = 2
class twai_status_info_t(ctypes.Structure):
_fields_ = [
('state', twai_state_t),
('msgs_to_tx', uint32_t),
('msgs_to_rx', uint32_t),
('tx_error_counter', uint32_t),
('rx_error_counter', uint32_t),
('tx_failed_count', uint32_t),
('rx_missed_count', uint32_t),
('rx_overrun_count', uint32_t),
('arb_lost_count', uint32_t),
('bus_error_count', uint32_t)
]
class twai_general_config_t(ctypes.Structure):
_fields_ = [
# Mode of TWAI controller
('mode', twai_mode_t),
# Transmit GPIO number
('tx_io', gpio_num_t),
# Receive GPIO number
('rx_io', gpio_num_t),
# CLKOUT GPIO number (optional, set to -1 if unused)
('clkout_io', gpio_num_t),
# Bus off indicator GPIO number (optional, set to -1 if unused)
('bus_off_io', gpio_num_t),
# Number of messages TX queue can hold (set to 0 to disable TX Queue)
('tx_queue_len', uint32_t),
# Number of messages RX queue can hold
('rx_queue_len', uint32_t),
# Bit field of alerts to enable (see documentation)
('alerts_enabled', uint32_t),
# CLKOUT divider. Can be 1 or any even number from 2 to 14 (optional, set to 0 if unused)
('clkout_divider', uint32_t),
# Interrupt flags to set the priority of the driver’s ISR. Note that to use the ESP_INTR_FLAG_IRAM,
# the CONFIG_TWAI_ISR_IN_IRAM option should be enabled first.
('intr_flags', c_int)
] |
@kdschlosser I need time to comprehend your ideas. |
I finally got around to tinkering some. during my porting of the Vector dbc file parser I cam across an issue that would require a large block of the code to be written over. This is because of I am really looking forward to having this can functionality without having to use an external interface. I will trim down the nuber of pins being used from 5 to 2. question i have is does this interface have the ability to wake the ESP32 if it detects network traffic? |
The use of the keywords https://www.mouser.com/datasheet/2/302/TJA1050-1127967.pdf is that the case? All of the CAN interface modules I have seen use a CAN interface chip and a CAN transceiver chip. If you take the MCP2515 modules for example. One of the ones that I have is made up of an MCP2525 interface chip and the TJA1050 chip. This provides me the CAN high and CAN low pins needed to connect to a CAN network. |
Yes, you need a CAN transceiver chip. I currently am using the SN65HVD230DR https://www.ti.com/lit/ds/symlink/sn65hvd230.pdf This is all you need as the ESP32 has the can controller built in, so there is no need for an interface chip. |
can you do me a favor and compile this for me. I am in need of the SPIRAM flavor. I am running a Windows box and setting up a Linux VM to compile it would be a pain. That is if my understanding is correct and it cannot be compiled on Windows. I want to give it a test run and I have an upcoming project I am trying to finish up and this would be a nice way to test it out. I have built a set of curve active and horizon active headlights for my car and I will be grabbing the steering angle from the electronic brake controller module in the vehicle and also the vehicle speed as well... does the can interface have the ability to wake the ESP32 from a deep sleep if the CAN network becomes active? |
@kdschlosser Here is the latest version I've been using, it isn't the spiram version. I can try to compile when I get to the next version
You can just get the ubuntu vm from the app store. it took a few seconds
Not that I'm aware of. It's something you'd have to do on your own I think. |
OK not a big deal about the wakeup. it would have been nice to have everything go to sleep when there is no data on the network and to have it wake up when there is traffic. I have already designed it to use a combination of the ignition, high beams, low beams, marker lights and turn signals to wake it up. I need to have the SPIRAM version in order to do what I am doing. The ESP controls a 23 pixel RGBW addressable led strip for the marker lights and a 40 pixel strip for the turn signal. It also controls the on and off for the high beams and low beams. There are 4 servos in each headlight housing, 2 lamps x and y control using a PCA9685 PWM controller. I also have a MPU9250 9 DOF MEMS sensor to get pitch and roll. the fusion algorithms are pretty ram intensive as it is all floating point math. I am also interfacing with the vehicles computer using CAN and it hosts a configuration webpage to adjust the headlights and change turn signal patterns/colors. The headlamp that hosts the config webpage is an AP and communicates to the other headlamp over a websocket connection. I am using a PCF8574T to switch up to 4 relays on and off, 2 relays for the headlights, one for the high beams and the last one turns on and off modules and the power supply for the LEDs. don't want any parasitic draw. It would be exceedingly difficult to do what I am trying to do with only 100K of RAM. |
I wanted to know if you wanted to add the bitrate to timing register code that I wrote to the CAN driver. It would need to be converted to C but it shouldn't be a major thing to do because it is not going to have to support more then the single interface using a 40mHz clock. It would be nice building in that feature so the proper timing registers can be made by supplying the bitrate, SJW and sample point. The SJW and sample point parameters would be defaulted to 1 for SJW and -1 for the sample point. If the sample point is -1 then a CIA compliant sample point for the given bitrate would be used. If that is added an autodetect feature could be added as well. Here is some pseudo code written in Python that would show how it would work. import time
import time
def autodetect_bitrate(sjw=1, samplepoint=-1):
for bitrate in range(10000, 1000000, 10000):
interface = CAN(bitrate, sjw, samplepoint)
count = 0
while not interface.has_messages:
count += 1
if count != 5:
time.sleep_ms(10)
continue
elif interface.error:
count = 5
break
if count == 5:
interface.close()
continue
break
else:
raise RuntimeError('Unable to autodetect bitrate')
return interface The above would be something that should be written in C code with the rest of the CAN module code. Also, you should be a note in the docs to use an MCP 2561 CAN transceiver. The reason why is the 2561 has a voltage reference input so it can match the RX and TX output voltages against. This way no level shifter on the I2C lines would need to be used. |
Quick question on this. I set up a VM and got it compiled. I have not tested it at all yet as I am making a stub file for it and I have a question. This is what defines the arguments passed to the //init// method enum {
ARG_mode,
ARG_extframe,
ARG_baudrate,
ARG_prescaler,
ARG_sjw,
ARG_bs1,
ARG_bs2,
ARG_tx_io,
ARG_rx_io,
ARG_tx_queue,
ARG_rx_queue,
ARG_auto_restart
};
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_mode, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = TWAI_MODE_NORMAL} },
{ MP_QSTR_extframe, MP_ARG_BOOL, {.u_bool = false} },
{ MP_QSTR_baudrate, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 500000} },
{ MP_QSTR_prescaler, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = CAN_DEFAULT_PRESCALER} },
{ MP_QSTR_sjw, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = CAN_DEFAULT_SJW} },
{ MP_QSTR_bs1, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = CAN_DEFAULT_BS1} },
{ MP_QSTR_bs2, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = CAN_DEFAULT_BS2} },
{ MP_QSTR_tx, MP_ARG_INT, {.u_int = 17} },
{ MP_QSTR_rx, MP_ARG_INT, {.u_int = 15} },
{ MP_QSTR_tx_queue, MP_ARG_INT, {.u_int = 1} },
{ MP_QSTR_rx_queue, MP_ARG_INT, {.u_int = 1} },
{ MP_QSTR_auto_restart, MP_ARG_BOOL, {.u_bool = false} },
}; I am seeing a bunch of keyword only arguments placed before positional ones , how would one go about doing that in python? If a keyword only argument doesn't have a default and there are positional arguments before it that do then there would be a need to create overloaded methods in the stub files. In this case the keyword only arguments should be moved to the bottom of the argument list and this would make things a bit easier to create a stub file for. This will enable an IDE to shop the correct parameters and how they should be passed. By having this kind of an arrangement a plethora of overloaded methods are going to have to be made to handle it properly. I can now understand as to why the included stub files are incorrect in the PyCharm plugin and that's because of this kind of a thing being done. Is there some explicit reason why the arguments are ordered in this manner? |
I am really going over this thing now and there are definitely some changes that do need to be made. An example is having the parameter extframe being passed to the constructor for the CAN class. extframe then gets set as an instance attribute and is used when sending a frame. there are networks that have mixed frame types being sent on the wire and the user should be able to choose what kind of frame they want to send the extframe. also the flags member in twai_message_t has been deprecated and shouldn't be used. I will make some changes to them and have you look over it and see if everything is good to go from the changes I make. I will also populate the exceptions that get raised and have None being passed to them. |
OK Here is a first draft. I have not yet compiled it or even check it for typos so I do not know if it will run. I wanted to get you a rough draft of it and you can make some suggestions/corrections if you see anything that is not right. it is attached at the bottom of this post Removed the rx callback as I am not sure how this would work because there is no interrupt in the esp-idf docs for a receive interrupt. Added support for bus off interrupt. I asked previously if the ESP32 TWAI interface supported waking and sleeping the device if the network goes to sleep, Well this is what does it. a pin number gets supplied and that pin will raise or lower depending on the state of the bus. I am guessing that this pin would be looped into another pin and that pin is what gets used for waking the ESP and also to see if the ESP can go to sleep. It doesn't mention in any great detail how to exactly use this feature so I will have to tinker about with it. Might just be able to read the pin/ set it to wake a deep sleet and nothing gets connected to it dunno. corrected the send method to have some additional parameters. setting if the frame is an extended frame can now be done on a message by message basis, this allows for a CAN network that has mixed CAN 1.0 and CAN 2.0 devices to be able to communicate with the ESP using a single interface and connection to the network. I also added checking if the frame id is the correct number of bits based on the frame being an extended frame or not. removed the traceback for having a data packet of > 8 bytes. The 8 bytes is from the specification and it is not a limitation of what a device is capable of doing so data packets of > 8 bytes can be sent they would not be ISO compliant. The non ISO compliant flag gets set and the full data packet gets sent even if it is > 8 bytes. added the single shot parameter, this disables trying to resend the packet if it fails. Changed up the recv method some as it was confusing as to how it worked and it actually looked like it would chew up quite a bit of memory in order to work. an optional data buffer can be passed and optional timeout as well. The data buffer can be bytes, bytearray, memoryview or None. if it is None a bytearray of the proper size will be created. If using any of the other data types it's length must be greater then the data packet that is being received. The length of the buffer is not sized and will remain the same. The reason why I do not have to size the buffer is because the function returns a tuple and in that tuple is the length of the data. The returned value from this method is a tuple, the tuple contains the frame id, is remote frame flag, is extended frame flag, the data length and the data as a bytearray, bytes or memoryview OK so now the big change I made. I added code that calculates the prescaler, time segment 1 and time segment 2 values that are needed to set a specific bitrate.. YAY!! so now if I wanted to set a bitrate that does not have a macro set up for it I am able to. A user is not going to have to hunt down a calculator on the internet to calculate the which I do not even know if one exists outside of a really confusing excel spreadsheet I had come across at one time. The calculations rely on knowing what the revision of the ESP is as well as what model of ESP it is (S2 and other). The CAN interface will not function properly without those things being explicitly set during the compiling of MicroPython. The makefile is going to have to be changed to allow the IDF_TARGET to get set to either esp32 or esp32s2 and also allow the CONFIG_ESP32_REV_MIN macro to get set to 1, 2, 3, or 4 (I believe it goes up to 4). If this is not done and revision 1 is used and the board is set to an esp 32 and the actual hardware is an esp32s2v2 then the calculations will be incorrect resulting in the TWAI interface being passed the wrong timings needed to get to the bitrate that is wanted. I added the machine_hw_calculate_bitrate_registers function for the purpose of only calculating the timings. it's use if fully commented in the source file. I extend the constructor in a manner that allows for the bit timing to be calculated using a reduced set of parameters If the baudrate is set to 0 then the brp, sjw, bs1, bs2 and triple_sample parameters are used to set the CAN interface. If the baudrate aligns with one of the macros in the esp-idf then that macro is used. and if there is no match then the timings are calculated using the baudrate alone using the CIA complaint sample point for that baudrate. the user supplied sjw is used as well as the triple_sample. there is a +- 2% margin of error for the match so if the exact match cannot be found for the given baudrate the next closest available baudrate is used which may be lower or higher depending on what is closest to the supplied value. I do not know if there is any specific reason for the ordering of the parameters to make MicroPython portable between different hardware. That is kind of hard to do with something like a CAN interface so I just changed it so it can support most of the features available. |
Hi, great work! I was waiting for a TWAI/CANBUS to use/test on ESP32 :)
Thank you do much. |
You MUST have transceivers even if the comms are between 2 ESP32's In the scripts current design it only supports a handful of bitrates out of the box. You do have the capability of setting custom bitrates but you would need to calculate what the prescalar, tseg1 and tseg 2 values need to be... I am working on a version that has this feature built into it. I am also working on adding support using an interrupt pin to notify when the bus has done to sleep or woken up. The thing I am unsure of with the use of this pin is if it can be used to put the ESP32 to sleep or not and if the interface remains powered up when the ESP is sleeping so it would have the ability to wake the ESP. The documentation does not say anything about this nor does it provide any examples for using it. I have not used this personally but the person that has created the PR states that it does work. make sure you get a transceiver that has a reference line for the TX and RX pins. The reference line attaches to the 3.3v power source for the ESP and it will set the output power of the TX and RX lines accordingly. If you get a transceiver without the reference you will need a 2 channel level shifter to change the voltage on the RX and TX lines if the transceiver is a 5v IC. The MCP2651 made by Microchip has a reference pin. That IC is the only thing that needs to be added in order to get a fully functioning CAN interface, no caps are needed and no clock crystal. Everything is built into the ESP32. The CAN interface uses the 80mhz clock in the ESP32 so it will integrate with just about every CAN network that you see commonly used. There are cheap MCP2515 can interfaces available that can be added onto the ESP32 using SPI. The issue with these interface is the 8mhz crystal that they use. it limits what the clock divider can be. Id you use an ESP32 < revision 2 you are limited to 64 dividers. >= revision 2 you have 95 dividers available and if you use an ESP32s2 you get a whopping 16384 dividers |
Also I do know what asyncio is. It is a way for a single thread to run code in what appears to be a parallel manner. That is fine and dandy and all if you have intentional places in the code where the running of the code needs to be stalled. In the example I provide we do not want to stall the running of the code at all so asyncio would not work for this use case. Things work a tad different when writing code for a PC vs an MCU. The MCU hs no where near the same speed as a PC so if you are receiving 6000 CAN frames a second you better believe that asyncio is not going to be able to do its job properly. It would cause all kinds of lost and dropped CAN messages due to it having to wait until the code that is running can wait so the messages can then be received. The buffers aren't that large. Even in my example if I do not parse the received messages often enough the ESP32 will run out of memory. Due to the nature of CAN I would recommend using an ESP32 that has 4mb of SPIRAM that can be used. 100K of ram for a typical ESP32 will vanish pretty quickly. |
Dear @kdschlosser, from machine import CAN
from micropython import const
import time
import gc
CAN_RX = const(15)
CAN_TX = const(17)
OUTGOING_FRAME_ID = const(30)
can = CAN(0,
mode=CAN.NORMAL,
baudrate=1000000,
tx=CAN_TX,
rx=CAN_RX
)
can.send( [1, 2, 3, 4], OUTGOING_FRAME_ID)
numreads=1
while numreads:
if can.any():
frame_id, rtr_frame, _, data = can.recv(timeout=10)
print("ID: ", frame_id, " DATA: ", data[:-1].encode('utf-8'), ':', (num_packets * 255) + data[7])
else:
time.sleep_ms(10)
numreads-=1
can.info() # get information about the controller’s error states and TX and RX buffers
can.deinit() # turn off the can bus
can.clear_rx_queue() # clear messages in the FIFO
can.clear_tx_queue() # clear messages in the transmit buffer
gc.collect() However, if I try to change the array of ints to bytearray or anything else I get TypeError. Also, I had to look into esp32_can.py to realize that the order of the can.send arguments was inverted. Don't get me wrong, I'm really happy to see that the module is going to be imported, and I would like to help others like me to start using it. Can you provide minimal (working) examples and some explaination of the API? |
By the way, according to @Tbruno25 here, it seems that Pycom's firmware can be used on ESP32, and that CAN bus is already implemented. Most interestingly (at least for @beyonlo and others interested in fastest response) it appears that Pycom's implementation exposes a callback function for receiving messages |
well considering that the version of MicroPython that is being distributed by that person is not legal and it is also a compiled binary file there is no way to see what has been modified. If you read this post espressif/esp-idf#7603 (comment) according to the folks over at Expressif an interrupt for received messages has not been implemented yet making it not possible at the current state of the esp-idf. That being said if there is a modification that has been done either to the esp-idf or something has been done to get it to work that's great! I am not sure why it hasn't been submitted as a PR into the esp-idf... The other issue is the esp-idf is releasing new versions like it's going out of stye there have been 17 new version of the esp-idf released since that binary was compiled. that's not a small number. There has been a HUGE number of fixes and updates. I am not the person that wrote this particular pull request. You mention documentation.. This is a pull request and documentation is not available for it because it has not been merged and released. In the example I posted I did state that it was pseudo code and that it has not been tested correct? The only thing you have told me is that it doesn't work. Not much to go on there, need to know what the errors are in order to correct the script. If you make changes to the script I am not going to be able to assist you at all with getting it to work because I am not going to know what changes have been made. Are you using a transceiver? if so who makes it and what is the part number for it? |
Hi @kdschlosser, For what concerns Pycom's firmware, I believe the code is here and here, but both MicroPython and ESP-IDF are forks of the originals, and they follow a different versioning number, so it is not easy to track how much "old" they are... for sure IDF is based on v3 instead of v4. It could still be useful to bring inspiration. For what concerns the interrupt, I'm quite sure that ESP32 has an internal interrupt for events captured by the CAN/TWAI peripheral, among which the arrival of a new packet. In the corresponding ISR the python callback can be called. This enables me and @beyonlo to read packets as fast as possible, without relying on polling the queue with any(). It is explained in Pycom's doc.
Finally: I've tried your example with threads and I don't really know how to help/debug. If I run the script "interactively" using Thonny I get a whole bunch of errors concerning Thonny itself, so I guess it does not handle correctly the use of threads. If I save your example in main.py and I reboot, the first error is "AttributeError: type object 'CAN' has no attribute 'MODE_NORMAL'". That can be easily fixed, but then you receive "TypeError: function missing 1 required positional arguments", which leads to the proper code self._interface = machine.CAN(0,
mode=machine.CAN.NORMAL,
baudrate=bitrate,
tx=tx,
rx=rx,
) Immediately after I get
and that is why I've previously claimed that I need array of ints instead of bytearray or anything else. I hope my experience is helping someone else. Here is the fixed example (change extension to .py) Now, time to bring up a second ESP32 and connect them with transceiver (not done yet...) |
Now I am not sure what the ink is that you posted for PYCOM and the CAN interface. It doesn't look like micropython code. It also does not use the ESP-IDF TWAI driver. I also cannot find any function to register a callback for a received message. If you look in the ESP-IDF documentation for the TWAI driver you will not find a way to register for a callback for a received message either. It's kind of a dead end I am thinking. I did find that you can use the Also only have those 2 problems with something I took 10 minutes to hammer out it pretty good. The MODE_ thing was kinda dumb and it was how I copied it from the ESP-IDF and not looking at the qstr in micropython to see if it was changed. Why there is an error when using a byte array is beyond me. The line of code that parses that argument is |
For the TWAI docs located here: I am not sure you were aware of this. |
I wasn't aware of that and that causes even further complications. I also noticed that there is only a 64 byte message buffer. That means there is enough space to hold 4 messages so if the buffer doesn't have a message removed within 100 microseconds (0.0001 seconds) for classic CAN or in 130 microseconds (0.00013 seconds) for extended CAN and the buffer happens to be full it is going to overrun. That is why I believe that when the interrupt occurs the buffer should be emptied in c code. it makes managing the memory use easier to do after all memory cannot be allocated during an interrupt unless the data gets put into an already created buffer. This would not be able to be done on the python end of things. The callback function would have to set a timer in order to receive the data and empty the buffer. There is going to be missed messages if the network is heavily trafficked if it is left that way. Ideally a buffer should be created that is a size the user specifies so the receive buffer can be emptied when the interrupt takes place and then the callback can occur and when the user calls receive it fetches the messages from the queue that was made in c code. The reason why the user should be able to set the size is because of how much network traffic there is. the more network traffic there is the larger the buffer would need to be in order to keep from missing messages. |
Correctly handle settings.toml that ends without a newline
Anyone have any ideas why this build is failing? Looks like this is what is keeping uPython from having proper CAN / TWIA support? |
Also keen to see if this can get into a mergeable state as it would be a shame if this very useful PR stalled |
"Only those with write access to this repository can merge pull requests." Not sure whats failing. |
Hello, /home/valentin/micropython/ports/esp32/machine_can.c:62: warning: "CAN_MODE_SILENT_LOOPBACK" redefined
#define CAN_MODE_SILENT_LOOPBACK 0x10
/home/valentin/micropython/ports/esp32/machine_can.c:51: note: this is the location of the previous definition
#define CAN_MODE_SILENT_LOOPBACK (0x10)
In file included from /home/valentin/micropython/ports/esp32/machine_can.c:45:
/home/valentin/micropython/ports/esp32/machine_can.c:81:68: error: 'CAN_MODE_NORMAL' undeclared here (not in a function); did you mean 'TWAI_MODE_NORMAL'?
.general = TWAI_GENERAL_CONFIG_DEFAULT(GPIO_NUM_2, GPIO_NUM_4, CAN_MODE_NORMAL),
^~~~~~~~~~~~~~~
/home/valentin/esp-idf/components/driver/include/driver/twai.h:32:77: note: in definition of macro 'TWAI_GENERAL_CONFIG_DEFAULT'
#define TWAI_GENERAL_CONFIG_DEFAULT(tx_io_num, rx_io_num, op_mode) {.mode = op_mode, .tx_io = tx_io_num, .rx_io = rx_io_num, \
^~~~~~~
In file included from /home/valentin/esp-idf/components/driver/include/driver/twai.h:21,
from /home/valentin/micropython/ports/esp32/machine_can.c:45:
/home/valentin/micropython/ports/esp32/machine_can.c: In function 'esp32_hw_can_init_helper':
/home/valentin/esp-idf/components/hal/include/hal/twai_types.h:77:41: error: expected expression before '{' token
#define TWAI_FILTER_CONFIG_ACCEPT_ALL() {.acceptance_code = 0, .acceptance_mask = 0xFFFFFFFF, .single_filter = true}
^
/home/valentin/micropython/ports/esp32/machine_can.c:221:28: note: in expansion of macro 'TWAI_FILTER_CONFIG_ACCEPT_ALL'
self->config->filter = TWAI_FILTER_CONFIG_ACCEPT_ALL();
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[ 98%] Building C object esp-idf/main/CMakeFiles/__idf_main.dir/__/network_ppp.c.obj
/home/valentin/micropython/ports/esp32/machine_can.c: In function 'esp32_hw_can_clearfilter':
/home/valentin/esp-idf/components/hal/include/hal/twai_types.h:77:41: error: expected expression before '{' token
#define TWAI_FILTER_CONFIG_ACCEPT_ALL() {.acceptance_code = 0, .acceptance_mask = 0xFFFFFFFF, .single_filter = true}
^
/home/valentin/micropython/ports/esp32/machine_can.c:600:28: note: in expansion of macro 'TWAI_FILTER_CONFIG_ACCEPT_ALL'
self->config->filter = TWAI_FILTER_CONFIG_ACCEPT_ALL();
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[ 98%] Building C object esp-idf/main/CMakeFiles/__idf_main.dir/__/network_lan.c.obj
/home/valentin/micropython/ports/esp32/machine_can.c: In function 'esp32_hw_can_setfilter':
/home/valentin/micropython/ports/esp32/machine_can.c:655:9: error: implicit declaration of function '_esp32_hw_can_set_filter'; did you mean 'esp32_hw_can_setfilter'? [-Werror=implicit-function-declaration]
_esp32_hw_can_set_filter(self, id, mask, args[ARG_bank].u_int, args[ARG_rtr].u_int);
^~~~~~~~~~~~~~~~~~~~~~~~
esp32_hw_can_setfilter
/home/valentin/micropython/ports/esp32/machine_can.c:657:13: error: 'bank' undeclared (first use in this function); did you mean 'mask'?
if (bank > ((self->extframe && self->config->filter.single_filter) ? 0 : 1 )) {
^~~~
mask
/home/valentin/micropython/ports/esp32/machine_can.c:657:13: note: each undeclared identifier is reported only once for each function it appears in
/home/valentin/micropython/ports/esp32/machine_can.c:662:13: error: 'addr' undeclared (first use in this function)
addr = (addr & 0x1FFFFFFF) << 3 | (rtr ? 0x04 : 0);
^~~~
/home/valentin/micropython/ports/esp32/machine_can.c:662:48: error: 'rtr' undeclared (first use in this function); did you mean 'qstr'?
addr = (addr & 0x1FFFFFFF) << 3 | (rtr ? 0x04 : 0);
^~~
qstr
cc1: some warnings being treated as errors
make[3]: *** [esp-idf/main/CMakeFiles/__idf_main.dir/build.make:3147: esp-idf/main/CMakeFiles/__idf_main.dir/__/machine_can.c.obj] Error 1
make[3]: *** Waiting for unfinished jobs....
make[3]: Leaving directory '/home/valentin/micropython/ports/esp32/build-GENERIC'
make[2]: *** [CMakeFiles/Makefile2:4856: esp-idf/main/CMakeFiles/__idf_main.dir/all] Error 2
make[2]: Leaving directory '/home/valentin/micropython/ports/esp32/build-GENERIC'
make[1]: *** [Makefile:136: all] Error 2
make[1]: Leaving directory '/home/valentin/micropython/ports/esp32/build-GENERIC'
make failed with exit code 2
-e See https://github.com/micropython/micropython/wiki/Build-Troubleshooting
make: *** [Makefile:40: all] Error 1 I Hope somebody can help me. |
what version of the ESP IDF are you using? you need to be using 4 not 5 |
I use version 4.4.5 |
Use this PR instead. It is more up to date than this one. |
Thanks I will try it tomorrow and share my results |
Code size report:
|
This PR failed due to rebasing. Go to the port/esp32: Add CAN(TWAI) driver. #12331 |
Thanks for your efforts keeping this PR up to date, @IhorNehrutsa. I'm personally very keen to have CAN support on the esp32 port, so your work is much appreciated! We now have a planned way forward for a If this PR is no longer being updated in favour of the PRs linked above then I'll close it now, as well. |
This pull request is based on #5310
#include "driver/twai.h"
instead of deprecated
#include "driver/can.h"
This PR failed due to rebasing. Go to the port/esp32: Add CAN(TWAI) driver. #12331