Я пытаюсь использовать cantools в Python для декодирования сообщения. В каждом примере, который я вижу, используется файл dbc «tests/files/dbc/motohawk.dbc» или «путь к файлу dbc». Вам необходимо приобрести этот файл или его содержимое? Если нет, то как получить файл, который будет декодировать PGN NMEA2000? В комплект Cantools входит такой?
import cantools
from pprint import pprint
db = cantools.database.load_file('tests/files/dbc/motohawk.dbc')
db.messages
example_message = db.get_message_by_name('ExampleMessage')
pprint(example_message.signals)`
В конечном итоге меня интересует анализ PGN 127501/127502 для стандартной цифровой коммутации NMEA 2000, а не Maretron, CZone или Empribus.
Для освещения я использую контроллер YachtDevices YDSC и релейный модуль Oceanic. Но мне бы хотелось, чтобы приложение управляло вещами рядом с моим картплоттером Garmin.
На сайте cantools (https://pypi.org/project/cantools/) есть много примеров, но все они ссылаются на файлtest/files/dbc/motohawk.dbc, который для меня не существует.
Итак, вопрос двоякий:






После разговора с NMEA2000.org я узнал, что их база данных необходима для покупки файла dbc NMEA2000 у стороннего производителя. База данных NMEA2000 стоит очень дорого, в тысячах долларов. Поэтому я решил пойти в другом направлении и расшифровать его самостоятельно.
При использовании Python Can структура сообщений can в библиотеке can работает отлично. Используя этот документ github для структур NMEA2000 PGN, вы можете делать все, что захотите. Github может реализовать проект.
Продолжая отлаживать эту проблему, я смог найти работающее доказательство концепции скрипта Python. Он был разработан на Raspberry Pi 4 B с разъемом PiCAN-M для интерфейса NMEA2000. Вы можете заменить строку инициализации can0 для последовательного порта/USB и использовать ActiSense NGT-1.
Также необходимы графические кнопки или просто измените их на цвета для включения/выключения. Он был разработан с использованием стандартного оборудования NMEA2000, YachtDevices YDSC-04 (для переключателей) и релейного модуля Oceanic Systems (для освещения). Он поддерживает стандартную цифровую коммутацию NMEA2000, а не проприетарные.
Это конкретно касается отправки PGN127501 (состояние переключателя) и PGN127502 (состояние переключателя). Он также декодирует 127502 для обновления состояния переключателя. Интерфейс представляет собой графический графический интерфейс с сенсорным экраном на 8 переключателей. Существует оператор(ы) отладки печати, который печатает все полученные номера PGN, без данных. PGN127501 не декодируется в этом доказательстве концепции, но структура данных такая же, как и у 127502, поэтому для этого существует код.
from tkinter import *
import tkinter.font as fnt
import array as arr
import can
import time
import os
import threading
from bitstring import BitArray
from can import Message
def button_click(btn):
if btn == 'b1':
if onoff[0]==1:
button1.config(image=imgon)
onoff[0]=2
snd501(b'\xfd\xff')
snd502(b'\xfd\xff')
else:
button1.config(image=imgoff)
onoff[0]=1
snd501(b'\xfc\xff')
snd502(b'\xfc\xff')
elif btn == 'b2':
if onoff[1]==1:
button2.config(image=imgon)
onoff[1]=2
snd501(b'\xf7\xff')
snd502(b'\xf7\xff')
else:
button2.config(image=imgoff)
onoff[1]=1
snd501(b'\xf3\xff')
snd502(b'\xf3\xff')
elif btn == 'b3':
if onoff[2]==1:
button3.config(image=imgon)
onoff[2]=2
snd501(b'\xdf\xff')
snd502(b'\xdf\xff')
else:
button3.config(image=imgoff)
onoff[2]=1
snd501(b'\xcf\xff')
snd502(b'\xcf\xff')
elif btn == 'b4':
if onoff[3]==1:
button4.config(image=imgon)
onoff[3]=2
snd501(b'\x7f\xff')
snd502(b'\x7f\xff')
else:
button4.config(image=imgoff)
onoff[3]=1
snd501(b'\x3f\xff')
snd502(b'\x3f\xff')
elif btn == 'b5':
if onoff[4]==1:
button5.config(image=imgon)
onoff[4]=2
snd501(b'\xff\xfd')
snd502(b'\xff\xfd')
else:
button5.config(image=imgoff)
onoff[4]=1
snd501(b'\xff\xfc')
snd502(b'\xff\xfc')
elif btn == 'b6':
if onoff[5]==1:
button6.config(image=imgon)
onoff[5]=2
snd501(b'\xff\xf7')
snd502(b'\xff\xf7')
else:
button6.config(image=imgoff)
onoff[5]=1
snd501(b'\xff\xf3')
snd502(b'\xff\xf3')
elif btn == 'b7':
if onoff[6]==1:
button7.config(image=imgon)
onoff[6]=2
snd501(b'\xff\xdf')
snd502(b'\xff\xdf')
else:
button7.config(image=imgoff)
onoff[6]=1
snd501(b'\xff\xcf')
snd502(b'\xff\xcf')
elif btn == 'b8':
if onoff[7]==1:
button8.config(image=imgon)
onoff[7]=2
snd501(b'\xff\x7f')
snd502(b'\xff\x7f')
else:
button8.config(image=imgoff)
onoff[7]=1
snd501(b'\xff\x3f')
snd502(b'\xff\x3f')
def snd501(BANKSTATUS):
msg = Message(data=bytearray(BANK.to_bytes(1,'big') + BANKSTATUS + BLANK),
arbitration_id=PGN501,
dlc=8,
channel='can0',
timestamp=time.time(),
is_extended_id=True)
bus.send(msg)
print('PGN SENT: ' + PGN501 + ' ID: ' + str(msg.arbitration_id) + ' Data: ' + str(msg.data ))
def snd502(BANKSTATUS):
msg = Message(data=bytearray(BANK.to_bytes(1,'big') + BANKSTATUS + BLANK),
arbitration_id=PGN502,
dlc=8,
channel='can0',
timestamp=time.time(),
is_extended_id=True)
bus.send(msg)
print('PGN SENT: ' + PGN502 + ' ID: ' + str(msg.arbitration_id) + ' Data: ' + str(msg.data ))
def getpgn(pgn):
pgn=bin(pgn) #Get bin of the id
pgn = pgn[2:len(pgn)] # strip off the '0b'
while len(pgn) < 29 : # make sure it's 29 bits
pgn = '0' + pgn
pgn = pgn[3:-8] #get the middle 18 bits
pgn = int(pgn, 2) #convert to int
return pgn
def handle_data(PGN, data):
if PGN == 127502:
msgData = data
if getBank(msgData) == BANK:
bankstatus = str(data)
# print(bankstatus[18:-26])
# print(bankstatus[22:-22])
bankstatus1 = bankstatus[18:-26]
SetBank1Value(bankstatus1)
bankstatus2 = bankstatus[22:-22]
SetBank2Value(bankstatus2)
if PGN == 127501:
msgData = data
if getBank(msgData) == BANK:
bankstatus = str(data)
print(bank status)
def SetBank1Value(bankstatus):
if bankstatus != 'ff':
if bankstatus == 'fd':
button1.config(image=imgon)
onoff[0]=2
elif bankstatus == 'fc':
button1.config(image=imgoff)
onoff[0]=1
elif bankstatus == 'f7':
button2.config(image=imgon)
onoff[1]=2
elif bankstatus == 'f3':
button2.config(image=imgoff)
onoff[1]=1
elif bankstatus == 'df':
button3.config(image=imgon)
onoff[2]=2
elif bankstatus == 'cf':
button3.config(image=imgoff)
onoff[2]=1
elif bankstatus == '7f':
button3.config(image=imgon)
onoff[3]=2
elif bankstatus == '3f':
button3.config(image=imgoff)
onoff[3]=1
def SetBank2Value(bankstatus):
if bankstatus != 'ff':
if bankstatus == 'fd':
button5.config(image=imgon)
onoff[4]=2
elif bankstatus == 'fc':
button5.config(image=imgoff)
onoff[4]=1
elif bankstatus == 'f7':
button6.config(image=imgon)
onoff[5]=2
elif bankstatus == 'f3':
button6.config(image=imgoff)
onoff[5]=1
elif bankstatus == 'df':
button7.config(image=imgon)
onoff[6]=2
elif bankstatus == 'cf':
button7.config(image=imgoff)
onoff[6]=1
elif bankstatus == '7f':
button8.config(image=imgon)
onoff[7]=2
elif bankstatus == '3f':
button8.config(image=imgoff)
onoff[7]=1
def getBank(data):
try:
bank = str(data)
bank = bank[13:-30]
bank = int('0' + bank, 0)
return bank
except:
return 999 #return an non exisant number
def read_from_port():
connected = False
try:
while not connected:
message = bus.recv()
for msg in bus:
PGN = getpgn(msg.arbitration_id)
if PGN == 127501 or PGN == 127502 :
print(str(PGN) + " : " + str(msg.arbitration_id) + " : " + str(msg.data ))
handle_data(PGN, msg.data)
else:
print(str(PGN) + " : " + str(msg.arbitration_id) + " : " + str(msg.data ))
except:
exit()
connected = True
def exit_app():
connected = True
bus.shutdown()
time.sleep(0.3)
os._exit
time.sleep(0.1)
app.destroy()
exit()
app = Tk()
# needed to be able to shutdown port
connected = False
PGN501 = 233966978
PGN502 = 233967275
# Switch Bank var's
BANK = 12 # BANK.to_bytes(1,'big')
BLANK = bytearray(b'\xff\xff\xff\xff\xff')
# Switches 1-->8
#lower nibble of first byte is 2,1 upper nibble is 4,3
#lower nibble of 2nd byte is 6,5 upper nibble is 8,7
#the mask to leave a byte unchanged is ff or f if a nibble.
# BANKSTATUS = bytearray(b'\xff\xff')
os.system("sudo /sbin/ip link set can0 up type can bitrate 500000")
time.sleep(0.1)
app.title("Reel Nauti Switching")
#app.geometry("1920x1080") #Window dimensions
app.geometry("1200x700") #Window dimensions
app.configure(background='black')
#image size is 202x315 XxY
imgblank = PhotoImage(file = r"./images/blank.png")
imgon = PhotoImage(file = r"./images/on.png")
imgoff = PhotoImage(file = r"./images/off.png")
spacer = 20
btnw = 202
btnh = 315
PGN = any
# define new font
newfont = fnt.Font(family='Arial', size=22, weight=fnt.BOLD)
# internal button state array 0=uninitated, 1=off, 2=on
onoff = arr.array('i',[0,0,0,0,0,0,0,0]) # array starts at 0 using 0-7
# Create button 1
button1 = Button(app, text = "Engine\nRoom\nLights", image=imgblank, borderwidth=0, font=newfont, compound = "center",
command=lambda m = "b1":button_click(m), cursor = "hand2",fg='white', bg='black', activebackground = "black",
activeforeground = "white", highlightbackground = "black", highlightcolor = "green", highlightthickness=0)
button1.pack()
button1.place(x=spacer , y=spacer)
# Create button 2
button2 = Button(app, text = "Courtesy\nLights", image=imgblank, borderwidth=0, font=newfont, compound = "center",
command=lambda m = "b2":button_click(m), cursor = "hand2",fg='white', bg='black', activebackground = "black",
activeforeground = "white", highlightbackground = "black", highlightcolor = "green", highlightthickness=0,)
button2.pack()
button2.place(x=btnw + (2 * spacer) , y=spacer)
# Create button 3
button3 = Button(app, text = "Spreader\nLights", image=imgblank, borderwidth=0, font=newfont, compound = "center",
command=lambda m = "b3":button_click(m), cursor = "hand2",fg='white', bg='black', activebackground = "black",
activeforeground = "white", highlightbackground = "black", highlightcolor = "green", highlightthickness=0,)
button3.pack()
button3.place(x=(2 *btnw) + (3 * spacer) , y=spacer)
# Create button 4
button4 = Button(app, text = "Under\nWater\nLights", image=imgblank, borderwidth=0, font=newfont, compound = "center",
command=lambda m = "b4":button_click(m), cursor = "hand2",fg='white', bg='black', activebackground = "black",
activeforeground = "white", highlightbackground = "black", highlightcolor = "green", highlightthickness=0,)
button4.pack()
button4.place(x=(3 * btnw) + (4 * spacer) , y=spacer)
# Create button 5 on second row
button5 = Button(app, text = "Arch\nLights", image=imgblank, borderwidth=0, font=newfont, compound = "center",
command=lambda m = "b5":button_click(m), cursor = "hand2",fg='white', bg='black', activebackground = "black",
activeforeground = "white", highlightbackground = "black", highlightcolor = "green", highlightthickness=0,)
button5.pack()
button5.place(x=spacer, y=btnh + (spacer * 2))
# Create button 6 on second row
button6 = Button(app, text = "Dash\nLights", image=imgblank, borderwidth=0, font=newfont, compound = "center",
command=lambda m = "b6":button_click(m), cursor = "hand2",fg='white', bg='black', activebackground = "black",
activeforeground = "white", highlightbackground = "black", highlightcolor = "green", highlightthickness=0,)
button6.pack()
button6.place(x= btnw + (2 * spacer) , y=btnh + (spacer * 2))
# Create button 7 on second row
button7 = Button(app, text = "Cockpit\nLights", image=imgblank, borderwidth=0, font=newfont, compound = "center",
command=lambda m = "b7":button_click(m), cursor = "hand2",fg='white', bg='black', activebackground = "black",
activeforeground = "white", highlightbackground = "black", highlightcolor = "green", highlightthickness=0,)
button7.pack()
button7.place(x= (2 * btnw) + (3 * spacer) , y=btnh + (spacer * 2))
# Create button 8 on second row
button8 = Button(app, text = "Salon\nHi-Hats", image=imgblank, borderwidth=0, font=newfont, compound = "center",
command=lambda m = "b8":button_click(m), cursor = "hand2",fg='white', bg='black', activebackground = "black",
activeforeground = "white", highlightbackground = "black", highlightcolor = "green", highlightthickness=0,)
button8.pack()
button8.place(x= (3 * btnw) + (4 * spacer) , y=btnh + (spacer * 2))
# Create exit button - where to put it?
button9 = Button(app, text = "Exit", command=exit_app)
button9.pack()
button9.place(x= (4 * btnw) + (5 * spacer) , y=btnh + (spacer * 2))
connected = False
try:
bus = can.interface.Bus(channel='can0', bustype='socketcan')
print('Ready')
except:
print('Init of PICAN failed')
# thread to handle the can port
thread = threading.Thread(target=read_from_port)
thread.start()
# Mainloop that will run forever
app.mainloop()
Пожалуйста, уточните вашу конкретную проблему или предоставьте дополнительную информацию, чтобы выделить именно то, что вам нужно. Поскольку сейчас написано, трудно точно сказать, о чем вы спрашиваете.