send_dbus.py
from pydbus import SessionBus
from gi.repository import GLib
class dbusService:
'''
DBus Service Example
'''
dbus = """
<node>
<interface name='com.example.dbusService'>
<method name='send'>
<arg type='i' name='distance' direction='out'/>
</method>
</interface>
</node>
"""
def __init__(self):
self.value = 100
def send(self) -> int:
self.value += 1
return self.value
bus = SessionBus()
bus.publish("com.example.dbusService", dbusService())
loop = GLib.MainLoop()
loop.run()
recv_dbus.py
from pydbus import SessionBus
import time
#from gi.repository import GLib
# Connect to the session bus
bus = SessionBus()
remote_object = bus.get('com.example.dbusService',"/com/example/dbusService")
while(1):
print(remote_object.send())
time.sleep(1)
위 코드와 같이 D-Bus 연결을 위해 세션버스 연결 및 경로(name)설정이 필요하다. 위 코드에서 경로는'com.example.dbusService' 이고 경로는 컴퓨터 내 공간이 아닌 D-bus의 이름을 할당해주는 것이기 때문에 컴퓨터 내부 경로와 혼동하지 말아야 한다.
받는쪽에서는 send측에서 정의한 경로를 통해 D-bus에 진입하고 메서드를 호출함으로써 데이터를 불러올 수 있다.
이유는 모르겠지만 send 파일을 처음에 만들 때 init 함수를 안만든채로 실행시켰더니 dbus가 실행되지 않았다. 꼭 필요한듯하다,,
다음 명령어를 통해 위 코드 실행이 가능하다.
python3 send_dbus.py &
python3 recv_dbus.py
다음은 CAN Data를 받아온 후 D-bus로 publish 하는 코드이다.
can_publish.py
import os
import can
from pydbus import SessionBus
from gi.repository import GLib
CAN_ID = "can0"
class dbusService:
'''
DBus Service Example
'''
dbus = """
<node>
<interface name='com.example.dbusService'>
<method name='getDis'>
<arg type='f' name='distance' direction='out'/>
</method>
<method name='getSpeed'>
<arg type='f' name='speed' direction='out'/>
</method>
</interface>
</node>
"""
def __init__(self):
os.system(f'sudo ifconfig {CAN_ID} down')
os.system(f'sudo ip link set {CAN_ID} up type can bitrate 500000 dbitrate 8000000 restart-ms 1000 berr-reporting on fd on')
self.can = can.interface.Bus(channel = CAN_ID, bustype = 'socketcan')
def getDis(self) -> float:
msg = self.can.recv();
if msg is None:
return "No message recieved"
distance = msg.data[2] + msg.data[3]*256
return distance
def getSpeed(self) -> float:
msg = self.can.recv();
if msg is None:
return "No message recieved"
rpm = msg.data[0] + msg.data[1]*256
speed = self.rpm2speed(rpm)
return speed
bus = SessionBus()
bus.publish("com.example.dbusService", dbusService())
loop = GLib.MainLoop()
loop.run()
위 코드는 아두이노가 보낸 CAN Data를 Raspberry pi를 통해 받고, 이를 pydbus를 사용하여 dbus내에 전송하는 코드이다. 위 코드에는 init 함수를 통해 CAN 환경을 재정의 하고 받은 CAN 데이터를 복호화하는 과정이 추가되었다.아두이노에서 보내는 CAN 통신 속도는 500k이기 때문에 bitrate를 500으로 맞춰주었고,CAN data frame 내 [0],[1] 바이트에 rpm 값을, [2],[3] 바이트에 Distance값을 넣어 전송했기 때문에 위와같은 복호화 과정을 거쳤다.
다음은 CAN Data를 전송한 아두이노 코드이다.
#include <SPI.h>
#include <mcp2515.h>
//--------distance--------------------
const int TRIG_PIN = 7;
const int ECHO_PIN = 6;
//------------rpm---------------------
const byte PulsesPerRevolution = 2;
const unsigned long ZeroTimeout = 100000;
const byte numReadings = 2;
volatile unsigned long LastTimeWeMeasured;
volatile unsigned long PeriodBetweenPulses = ZeroTimeout + 1000;
volatile unsigned long PeriodAverage = ZeroTimeout + 1000;
unsigned long FrequencyRaw;
unsigned long FrequencyReal;
unsigned int PulseCounter = 1;
unsigned long PeriodSum;
unsigned long LastTimeCycleMeasure = LastTimeWeMeasured;
unsigned long CurrentMicros = micros();
unsigned int AmountOfReadings = 1;
unsigned int ZeroDebouncingExtra;
unsigned long readings[numReadings];
unsigned long readIndex;
unsigned long total;
unsigned long average;
//-----------can----------------------------
struct can_frame speed;
MCP2515 mcp2515(9);
long whatisDistance(){
long duration, distance;
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
duration = pulseIn(ECHO_PIN,HIGH);
// convert the time into a distance
distance = duration / 29.1 / 2 ;
return distance;
}
void Pulse_Event() {
PeriodBetweenPulses = micros() - LastTimeWeMeasured;
LastTimeWeMeasured = micros();
if (PulseCounter >= AmountOfReadings) {
PeriodAverage = PeriodSum / AmountOfReadings;
PulseCounter = 1;
PeriodSum = PeriodBetweenPulses;
int RemapedAmountOfReadings = map(PeriodBetweenPulses, 40000, 5000, 1, 10);
RemapedAmountOfReadings = constrain(RemapedAmountOfReadings, 1, 10);
AmountOfReadings = RemapedAmountOfReadings;
}
else {
PulseCounter++;
PeriodSum = PeriodSum + PeriodBetweenPulses;
}
}
long whatisRpm(){
long RPM;
LastTimeCycleMeasure = LastTimeWeMeasured;
CurrentMicros = micros();
if (CurrentMicros < LastTimeCycleMeasure) {
LastTimeCycleMeasure = CurrentMicros;
}
FrequencyRaw = 10000000000 / PeriodAverage;
if (PeriodBetweenPulses > ZeroTimeout - ZeroDebouncingExtra || CurrentMicros - LastTimeCycleMeasure > ZeroTimeout - ZeroDebouncingExtra) {
FrequencyRaw = 0; // Set frequency as 0.
ZeroDebouncingExtra = 2000;
} else {
ZeroDebouncingExtra = 0;
}
FrequencyReal = FrequencyRaw / 10000;
RPM = FrequencyRaw / PulsesPerRevolution * 60;
RPM = RPM / 10000;
total = total - readings[readIndex];
readings[readIndex] = RPM;
total = total + readings[readIndex];
readIndex = readIndex + 1;
if (readIndex >= numReadings) {
readIndex = 0;
}
average = total / numReadings;
return RPM;
}
void setup() {
Serial.begin(115200);
//lcd.init();
//lcd.backlight();
attachInterrupt(digitalPinToInterrupt(2), Pulse_Event, RISING);
delay(1000);
//-------------distance setting---------------
pinMode(TRIG_PIN,OUTPUT);
pinMode(ECHO_PIN,INPUT);
//-------------can setting-------------------
mcp2515.reset();
mcp2515.setBitrate(CAN_500KBPS,MCP_8MHZ);
mcp2515.setNormalMode();
speed.can_id = 0x123;
speed.can_dlc = 8;
for(int i=0;i<8;i++){
speed.data[i] = 0;
}
}
void loop() {
long distance = whatisDistance();
long RPM = whatisRpm();
speed.data[0] = RPM%256;
speed.data[1] = RPM/256;
speed.data[2] = distance%256;
speed.data[3] = distance/256;
for(int i=0;i<8;i++){
Serial.print(speed.data[i]);
Serial.print(" ");
}
Serial.println();
mcp2515.sendMessage(&speed);
Serial.print("RPM: ");
Serial.print(RPM);
Serial.print(" ");
Serial.print("distance: ");
Serial.println(distance);
delay(200);
}