Skip to content

Commit

Permalink
Big restructuring
Browse files Browse the repository at this point in the history
  • Loading branch information
tillsc committed Jan 16, 2025
1 parent 5719008 commit a9fbbd6
Show file tree
Hide file tree
Showing 24 changed files with 294 additions and 75 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
__pycache__
.vscode
dist
Empty file added __init__.py
Empty file.
8 changes: 8 additions & 0 deletions __main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/python3

from can2mqtt.app import load_config, load_dbc_db, main_program

if __name__ == '__main__':
config = load_config()
dbc_db = load_dbc_db(config['dbc_files'])
main_program(config, dbc_db)
Empty file added build/lib/can2mqtt/__init__.py
Empty file.
28 changes: 28 additions & 0 deletions build/lib/can2mqtt/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import can
import cantools
import paho.mqtt.client as mqtt
import yaml
import threading
from can2mqtt.can_listener import CanListener

def load_config():
with open('config.yaml', 'r') as stream:
return yaml.load(stream)

def load_dbc_db(dbc_files):
db = cantools.database.Database()
for file in dbc_files:
with open (file, 'r') as fin:
db.add_dbc(fin)
return db

def main_program(config, dbc_db):
mqtt_client = mqtt.Client('can2mqtt')
mqtt_client.username_pw_set(username=config['mqtt']['username'],password=config['mqtt']['password'])
mqtt_client.connect(config['mqtt']['host'])

can_listener = CanListener(dbc_db, mqtt_client)
bus = can.interface.Bus(bustype='socketcan', channel=config['can']['interface'], bitrate=config['can']['bitrate'])
can.Notifier(bus, [can_listener])

threading.Event().wait()
38 changes: 38 additions & 0 deletions build/lib/can2mqtt/can_listener.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import can

class CanListener(can.Listener):

first_ts = 0
last = {}
first_underscores_to_slash = False
prefix = False

def __init__(self, db, mqtt_client, config):
if 'mqtt' in config:
if 'topic_names' in config['mqtt']:
if 'first_underscores_to_slash' in config['mqtt']['topic_names']:
self.first_underscores_to_slash = config['mqtt']['topic_names']['first_underscores_to_slash']
if 'prefix' in config['mqtt']['topic_names']:
self.prefix = config['mqtt']['topic_names']['prefix']
self.db = db
self.mqtt_client = mqtt_client

def on_message_received(self, m):
if self.first_ts == 0:
self.first_ts = m.timestamp

try:
msg = self.db.decode_message(m.arbitration_id, m.data)
for signal_id in msg:
topic = signal_id.lower()
if self.first_underscores_to_slash:
xxx
topic = topic.replace('_', '/', self.first_underscores_to_slash)
if self.prefix:
topic = self.prefix + topic
data = round(msg[signal_id], 5)
if topic not in self.last or self.last[topic]['data'] != data or m.timestamp - self.last[topic]['timestamp'] > 60:
self.last[topic] = {'data': data, 'timestamp': m.timestamp}
self.mqtt_client.publish(topic, data)
except KeyError:
pass
Empty file.
6 changes: 6 additions & 0 deletions build/lib/can2mqtt/test/mqtt_dummy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class MqttDummy():

def publish(self, topic, data):
self.topic = topic
self.data = data

24 changes: 24 additions & 0 deletions build/lib/can2mqtt/test/test_main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import unittest

import can
import struct

from can2mqtt.test.mqtt_dummy import MqttDummy
from can2mqtt.can_listener import CanListener
from can2mqtt.app import load_dbc_db

class TestStringMethods(unittest.TestCase):

def test_can_listener_converts_topic_names(self):
mqtt_dummy = MqttDummy()

config = {}
can_listener = CanListener(load_dbc_db(['can2mqtt/test/simple.dbc']), mqtt_dummy, config)

message = can.Message(arbitration_id=500, data=struct.pack('4b', 0, 0, 0, 50))
can_listener.on_message_received(message)
self.assertEqual(mqtt_dummy.topic, 'io/debug/test_unsigned')


if __name__ == "__main__":
unittest.main()
60 changes: 60 additions & 0 deletions can2mqtt.egg-info/PKG-INFO
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
Metadata-Version: 2.2
Name: can2mqtt
Version: 0.0.1
Summary: A python can to mqtt bridge
Home-page: https://github.com/tillsc/can2mqtt
Author: Till Schulte-Coerne
Author-email: [email protected]
Project-URL: Bug Reports, https://github.com/tillsc/can2mqtt/issues
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development ::
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Requires-Python: !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4
Description-Content-Type: text/markdown
Requires-Dist: python-can
Requires-Dist: cantools
Requires-Dist: paho-mqtt
Requires-Dist: pyyaml
Provides-Extra: dev
Provides-Extra: test
Requires-Dist: pytest; extra == "test"
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: keywords
Dynamic: project-url
Dynamic: provides-extra
Dynamic: requires-dist
Dynamic: requires-python
Dynamic: summary

[DBC](http://socialledge.com/sjsu/index.php/DBC_Format) based CAN to MQTT brigde
===

This is a generic CAN 2 MQTT bridge build with Python 3

### Setup

This package is based upon the following dependencies:

* [python-can](https://python-can.readthedocs.io/en/master/)
* [cantools](https://github.com/eerimoq/cantools)
* [phao](http://www.eclipse.org/paho/)

Install all required Python 3 dependencies on a raspberry:

sudo apt-get install python3-pip python3-can
pip3 install paho-mqtt cantools

### Usage

Copy your DBC files into the root directory.

Copy config.example.yaml to config.yaml and modify it.

Run `./main.py` or `python3 main.py`
14 changes: 14 additions & 0 deletions can2mqtt.egg-info/SOURCES.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
README.md
setup.py
can2mqtt/__init__.py
can2mqtt/app.py
can2mqtt/can_listener.py
can2mqtt.egg-info/PKG-INFO
can2mqtt.egg-info/SOURCES.txt
can2mqtt.egg-info/dependency_links.txt
can2mqtt.egg-info/entry_points.txt
can2mqtt.egg-info/requires.txt
can2mqtt.egg-info/top_level.txt
can2mqtt/test/__init__.py
can2mqtt/test/mqtt_dummy.py
can2mqtt/test/test_main.py
1 change: 1 addition & 0 deletions can2mqtt.egg-info/dependency_links.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

2 changes: 2 additions & 0 deletions can2mqtt.egg-info/entry_points.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[console_scripts]
run = run:main
9 changes: 9 additions & 0 deletions can2mqtt.egg-info/requires.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
python-can
cantools
paho-mqtt
pyyaml

[dev]

[test]
pytest
1 change: 1 addition & 0 deletions can2mqtt.egg-info/top_level.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
can2mqtt
Empty file added can2mqtt/__init__.py
Empty file.
28 changes: 28 additions & 0 deletions can2mqtt/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import can
import cantools
import paho.mqtt.client as mqtt
import yaml
import threading
from can2mqtt.can_listener import CanListener

def load_config():
with open('config.yaml', 'r') as stream:
return yaml.load(stream)

def load_dbc_db(dbc_files):
db = cantools.database.Database()
for file in dbc_files:
with open (file, 'r') as fin:
db.add_dbc(fin)
return db

def main_program(config, dbc_db):
mqtt_client = mqtt.Client('can2mqtt')
mqtt_client.username_pw_set(username=config['mqtt']['username'],password=config['mqtt']['password'])
mqtt_client.connect(config['mqtt']['host'])

can_listener = CanListener(dbc_db, mqtt_client)
bus = can.interface.Bus(bustype='socketcan', channel=config['can']['interface'], bitrate=config['can']['bitrate'])
can.Notifier(bus, [can_listener])

threading.Event().wait()
38 changes: 38 additions & 0 deletions can2mqtt/can_listener.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import can

class CanListener(can.Listener):

first_ts = 0
last = {}
first_underscores_to_slash = False
prefix = False

def __init__(self, db, mqtt_client, config):
if 'mqtt' in config:
if 'topic_names' in config['mqtt']:
if 'first_underscores_to_slash' in config['mqtt']['topic_names']:
self.first_underscores_to_slash = config['mqtt']['topic_names']['first_underscores_to_slash']
if 'prefix' in config['mqtt']['topic_names']:
self.prefix = config['mqtt']['topic_names']['prefix']
self.db = db
self.mqtt_client = mqtt_client

def on_message_received(self, m):
if self.first_ts == 0:
self.first_ts = m.timestamp

try:
msg = self.db.decode_message(m.arbitration_id, m.data)
for signal_id in msg:
topic = signal_id.lower()
if self.first_underscores_to_slash:
xxx
topic = topic.replace('_', '/', self.first_underscores_to_slash)
if self.prefix:
topic = self.prefix + topic
data = round(msg[signal_id], 5)
if topic not in self.last or self.last[topic]['data'] != data or m.timestamp - self.last[topic]['timestamp'] > 60:
self.last[topic] = {'data': data, 'timestamp': m.timestamp}
self.mqtt_client.publish(topic, data)
except KeyError:
pass
Empty file added can2mqtt/test/__init__.py
Empty file.
6 changes: 6 additions & 0 deletions can2mqtt/test/mqtt_dummy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class MqttDummy():

def publish(self, topic, data):
self.topic = topic
self.data = data

2 changes: 2 additions & 0 deletions can2mqtt/test/simple.dbc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
BO_ 500 IO_DEBUG: 4 IO
SG_ IO_DEBUG_test_unsigned : 0|8@1+ (1,0) [0|0] "" DBG
24 changes: 24 additions & 0 deletions can2mqtt/test/test_main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import unittest

import can
import struct

from can2mqtt.test.mqtt_dummy import MqttDummy
from can2mqtt.can_listener import CanListener
from can2mqtt.app import load_dbc_db

class TestStringMethods(unittest.TestCase):

def test_can_listener_converts_topic_names(self):
mqtt_dummy = MqttDummy()

config = {}
can_listener = CanListener(load_dbc_db(['can2mqtt/test/simple.dbc']), mqtt_dummy, config)

message = can.Message(arbitration_id=500, data=struct.pack('4b', 0, 0, 0, 50))
can_listener.on_message_received(message)
self.assertEqual(mqtt_dummy.topic, 'io_debug_test_unsigned')


if __name__ == "__main__":
unittest.main()
73 changes: 0 additions & 73 deletions main.py

This file was deleted.

Loading

0 comments on commit a9fbbd6

Please sign in to comment.