hardbyte / python-can

The can package provides controller area network support for Python developers
https://python-can.readthedocs.io
GNU Lesser General Public License v3.0
1.29k stars 599 forks source link

send_periodic multiple messages with broadcast manager #475

Closed Felhamed closed 5 years ago

Felhamed commented 5 years ago

I am using the MCP2515 CAN controller with MCP2551 CAN transceiver running socketCAN on a Raspberry pi.

Linux 4.14.52-v7+ #1123 SMP armv7l
dtparam=spi=on
dtoverlay=mcp2515-can0,oscillator=16000000,interrupt=25
dtoverlay=spi-bcm2835-overlay 

I am trying to send messages which will have 10 different arbitration IDs and 2 different sets of data at 200ms intervals using send_periodic and the broadcast manager method:

#!/usr/bin/python3
import can
import time
import logging
from datetime import datetime

logging.basicConfig(level=logging.INFO)

def periodic_send(msg, bus):
    task = bus.send_periodic(msg, 0.20)
    assert isinstance(task, can.CyclicSendTaskABC)

    return task

if __name__ == "__main__":
    bus = can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000)
    canlog = '/home/user/Python/logfile'
    listener = can.BufferedReader()
    logger = can.ASCWriter(canlog, channel='vcan0')
    notifier = can.Notifier(bus, [listener,logger])
    msg = listener.get_message(0.40)
    logger.log_event(msg, timestamp=None)

    start_can_id = int('181080F5', 16)
    stop_can_id = int('18108AF5', 16)
    can_ids = range(start_can_id, stop_can_id, 256)

    tasks = []

    for can_id in can_ids:
        msg_volt = can.Message(arbitration_id=can_id,
                               data=[1, 0, 0, 0, 0, 0, 0, 0],
                               extended_id=True)
        msg_temp = can.Message(arbitration_id=can_id,
                               data=[2, 0, 0, 0, 0, 0, 0, 0],
                               extended_id=True)
        tasks.append(periodic_send(msg_volt,bus))
        tasks.append(periodic_send(msg_temp,bus))

    while True:
        try:
            time.sleep(0.8)
        except KeyboardInterrupt:
            print('Stopping')
            break

However, I am not reliably transmitting the data, I can see in the logs that the task transmits data 1 and data 2 together however after several seconds all I get is data 2:

  vcan0  181081F5   [8]  02 00 00 00 00 00 00 00
  vcan0  181082F5   [8]  02 00 00 00 00 00 00 00
  vcan0  181080F5   [8]  02 00 00 00 00 00 00 00
  vcan0  181085F5   [8]  02 00 00 00 00 00 00 00
  vcan0  181086F5   [8]  02 00 00 00 00 00 00 00
  vcan0  181084F5   [8]  02 00 00 00 00 00 00 00
  vcan0  181087F5   [8]  02 00 00 00 00 00 00 00
  vcan0  181088F5   [8]  02 00 00 00 00 00 00 00
  vcan0  181089F5   [8]  02 00 00 00 00 00 00 00
hardbyte commented 5 years ago

We had an issue with the periodic sender which was worked on in the last release. Do you mind updating to the latest version of python-can and using the bus periodic send api.

Also can you confirm that the cyclic.py example works?

christiansandberg commented 5 years ago

Does Socketcan really support sending multiple messages with the same ID?

hardbyte commented 5 years ago

No I think you're right Christian:

The different RX/TX jobs are distinguished by the unique can_id in each BCM message.

I'm not 100% sure but if you used 2 BCM sockets (which you'd get with 2 can Bus instances) you might be able to do it

Felhamed commented 5 years ago

I was able to use a regular send(); to accomplish what I needed.

#!/usr/bin/python3
import can
import time
import logging
from datetime import datetime

logging.basicConfig(level=logging.INFO)

def periodic_send(msg, bus):
    task = bus.send(msg)

if __name__ == "__main__":
    bus = can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000)    
    start_can_id = int('181080F5', 16)
    stop_can_id = int('18108AF5', 16)
    can_ids = range(start_can_id, stop_can_id, 256)

    while True:
        try:
            for can_id in can_ids:
                msg_volt = can.Message(arbitration_id=can_id,
                                    data=[1, 0, 0, 0, 0, 0, 0, 0],
                                    extended_id=True)
                msg_temp = can.Message(arbitration_id=can_id,
                                    data=[2, 0, 0, 0, 0, 0, 0, 0],
                                    extended_id=True)
                periodic_send(msg_volt,bus)
                periodic_send(msg_temp,bus)
                time.sleep(0.10)
        except KeyboardInterrupt:
            print('Stopping')
            break

I also had to use separate sockets to do tranmistting/logging/and buffered listening at the same time.

if __name__ == "__main__":
    thread_log = threading.Thread(target=logging_loop)
    thread_log.start()

    thread_transmit = threading.Thread(target=transmit_loop)
    thread_transmit.start()

    thread_decode = threading.Thread(target=decoder_loop)
    thread_decode.start()
hardbyte commented 5 years ago

Well I'm glad you found something that accomplishes what you need.

That solution isn't ideal though as the timing is being managed by the host computer in Python rather than on the device or in the host kernel. Did you happen to try using bus.send_periodic with a separate temp_bus and volt_bus?