【RaspberryPi】 RaspberryPiとArduinoをCAN通信させた 2回目

少し前に"Rasbee オリジナル MCP2515 CAN バス モジュール TJA1050 レシーバーSPIモジュール Arduinoのための AVR"を1つ壊してしまったのでMCP2561とMCP2515を改めて購入。
組み立てができたのでpython-canを用いて、RaspberryPiとArduinoのCAN通信で遊んでみたのでメモ。

f:id:Yoshichi:20171001032155j:plain

ハード側の準備

構成

配線などはfritzingの使い方覚えたら公開
(が、配線は察しorz)
f:id:Yoshichi:20171001032248j:plain

Arduino

ソースはこんな感じ。

#include <mcp_can.h>
#include <SPI.h>

#define TRIG_PIN  8
#define ECHO_PIN  9
int Duration;
float Distance;

MCP_CAN CAN0(10);     // Set CS to pin 10
byte Txbuffer[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

void CanInit()
{
  // Initialize MCP2515 running at 16MHz with a baudrate of 500kb/s and the masks and filters disabled.
  if(CAN0.begin(MCP_ANY, CAN_500KBPS, MCP_16MHZ) == CAN_OK) Serial.println("MCP2515 Initialized Successfully!");
  else Serial.println("Error Initializing MCP2515...");

  CAN0.setMode(MCP_NORMAL);   // Change to normal mode to allow messages to be transmitted
}

void CanSetTxBuffer(int t_duration)
{
  int v;
  for(int i = 0; i < 8; i++) {
    Txbuffer[i] = 0x00;
  }
  v = max(0, t_duration);
  Txbuffer[0] = (v >> 8) & 0xFF;
  Txbuffer[1] = v & 0xFF;
}

int  CanTransmitt(int id)
{
  byte sndStat = CAN0.sendMsgBuf(id, 0, 8, Txbuffer);
  if(sndStat == CAN_OK) {
    return 1;
  } else {
    return 0;
  }
}

void HcSr4Init()
{
  pinMode(TRIG_PIN,OUTPUT);
  pinMode(ECHO_PIN,INPUT);
}

void HcSr4Run()
{
  digitalWrite(TRIG_PIN,LOW);
  delayMicroseconds(1);
  digitalWrite(TRIG_PIN,HIGH);
  delayMicroseconds(11);
  digitalWrite(TRIG_PIN,LOW);
  Duration = pulseIn(ECHO_PIN,HIGH);
  if (Duration>0) {
    Distance = Duration/2;
    Distance = Distance*340*100/1000000; // ultrasonic speed is 340m/s = 34000cm/s = 0.034cm/us 
  }
}

void setup()
{
  Serial.begin(9600);
  CanInit();
  HcSr4Init();
}

void loop()
{
  HcSr4Run();
  Serial.print(" Hex: ");
  Serial.println(Duration,HEX);
  Serial.print(Duration);
  Serial.print(" us ");
  Serial.print(Distance);
  Serial.println(" cm");

  CanSetTxBuffer(Duration);
  if(CanTransmitt(0x100) == 1){
    Serial.println("Message Sent Successfully!");
  } else {
    Serial.println("Error Sending Message...");
  }
  delay(500);
}

簡単な解説

距離センサモジュールで取得した応答時間
ID100に対してセットし、500ms毎に送信している。
応答時間が2byte程度で事足りそうだったので
ID100の1byte目に応答時間の上位バイト。
ID100の2byte目に応答時間の下位バイトをセットしている。
距離の算出は、とりあえずマニュアル通り。

RaspberryPi

まず、下記を実行

sudo ip link set can0 type can bitrate 500000

ソースはこんな感じ。

import RPi.GPIO as GPIO
import can
import time
from struct import *
GPIO.setmode(GPIO.BCM)
GPIO.setup(25, GPIO.IN)

bus = can.interface.Bus(channel = 'can0', bustype='socketcan_native', bitrate=500000, canfilters=None)

try:
    while True:
        if not(GPIO.input(25)):
            msg = bus.recv(0)
            if msg:
                duration = (msg.data[0] << 8) & 0xFF00
                duration |= msg.data[1] & 0xFF
                distance = duration // 2
                distance = distance * 340 * 100 / 1000000
                print(hex(duration))
                print(distance)
except KeyboardInterrupt:
     GPIO.cleanup()

簡単な解説

mcp2515のINTピン(割込みピン)をRaspberryPiのGPIO25に繋げているため
GPIO.input(25)で割込み判定を実施。
割り込み時はLowレベルになるので、notで判定しています。
後は、受信したデータをデシリアライズして出力しています。
※今思うと、ID100のみ受け取れるようにしないといけないですね。

実行結果

f:id:Yoshichi:20171002020454p:plain

左がArduinoの送信情報(シリアルモニタ)
右側がRaspberryPi(teraterm)の受信情報。
というわけで無事に取りえたい情報を通信できました。
また、距離センサーに手を近づけると、
それらしい反応を出しているのでやりたいことはできているっぽい。

補足

  • MCP2561は他のサイトでは、MCP2551で紹介されている。

端子の命名が一部変わっているものの、ほぼ同じものと考えて良いっぽい。

  • 紹介した、超音波距離センサモジュールは古いっぽいので

新しいものを買うのを推奨。

メモ

最近は専ら、RaspberryPiを使ってラジコンを作りたいと奮闘中。
上手いこと上記のやりとりをしながら自動停止とかしてみたい。。。