目次

フォークリフトシミュレータ(Arduino)

 フォークリフトのペダル、レバーの情報を定期的に
 Processingに送ればよいと考え、スイッチボードを
 Arduinoに接続します。



 スイッチボードの回路は、以下。




 左右に8ピンのDIPスイッチを配置してあるので
 ペダルとレバーの対応を仕様として決定。

 ペダル

  ペダルの操作情報は、左のスイッチを使って表現します。

  項目は、以下。

  4種の操作をビット割り当てします。



  power、parking brake、cratchは論理値でよいので
  1、0で表現し1ビット。

  acceleはアップ、ダウン、キープなので2ビット利用。

  各ペダルの情報は、個別では次の表現とします。

  8ビットの状態をリードして、16進数2桁で送信します。

 レバー

  レバーの操作情報は、右のスイッチを使って表現します。

  項目は、以下。

  4種の操作をビットに割り当てます。




  全レバーを2ビットで表示。

  各レバーの情報は、個別では次の表現とします。

  8ビットの状態をリードして、16進数2桁で送信します。

 仕様を決めたので、スケッチを作成しました。

#include <MsTimer2.h>

#define OFF 0
#define ON  OFF+1

#define LASTSTATE 2
#define LASTXCNT  10

#define LED_BIT 5

#define NPER 100

#define MASK0F 0x0f 

/* variables */
byte uflag ;
byte tflag ;
byte aflag ;
byte eflag ;
byte cmd ;
byte sindex ;
byte sbuf[4] ;
byte state ;
byte xswv[2];
byte swv[2] ;
byte xcnt ;
char msg[5];
byte tmpa ;
byte tmpb ;

/* function prototype */
void show_help();
char get_asc(byte x);
void rs_putchar(char x);
void rs_puts(char *ptr);
void crlf();
void send_led(byte x);
void update_trigger(void);
byte pre_rev(byte x);
byte rev(byte x);

void setup()
{
  /* initialize serial */
  Serial.begin(9600);
  sindex = 0 ;
  /* clear flags */
  uflag  = OFF ;
  tflag  = OFF ;
  aflag  = OFF ;
  eflag  = OFF ;
  /* initialize */
  for ( state = 0 ; state < LASTSTATE ; state++ ) {
    *(xswv+state) = 0 ;
    *(swv+state) = 0 ;
  }
  state = 0 ;
  xcnt  = 0 ;
  *(msg+4) = '\0' ;
  /* initialize port values */
  PORTB = 0xff ;
  PORTC = 0xf8 ;
  PORTD = 0x01 ;
  /* initialize port direction */
  DDRB = 0xff ;
  DDRC = 0x07 ;
  DDRD = 0xfe ;
  /* trigger period */
  MsTimer2::set(NPER,update_trigger);
  /* enable */ 
  MsTimer2::start();
}

void loop()
{
  /* scan switch */
  if ( aflag == ON ) {
    /* clear event flag */
    aflag = OFF ;
    /* default */
    tmpa = 0 ;
    tmpb = 0 ;
    /* get value */
    for ( int i = 0 ; i < 8 ; i++ ) {
      /* impress address */
      PORTC = (i & 7) ;
      /* delay */
      delayMicroseconds(10);
      /* get code */
      if ( PINC & 0x08 ) { tmpa |= ON ; }
      if ( PINC & 0x10 ) { tmpb |= ON ; }
      /* shift */
      if ( i < 7 ) {
        tmpa <<= 1 ;
        tmpb <<= 1 ;
      }
    }
    /* store */
    *(xswv+0) = rev( tmpa );
    *(xswv+1) = rev( tmpb );
  }
  /* transfer switch state */
  if ( tflag == ON ) {
    /* clear event flag */
    tflag = OFF ;
    /* copy */
    *(swv+0) = *(xswv+0) ;
    *(swv+1) = *(xswv+1) ;
    /* generate code */
    *(msg+0) = get_asc( *(swv+0) & MASK0F ) ;
    *(msg+1) = get_asc( (*(swv+0) >> 4) & MASK0F ) ;
    *(msg+2) = get_asc( *(swv+1) & MASK0F ) ;
    *(msg+3) = get_asc( (*(swv+1) >> 4) & MASK0F ) ;
    /* judge transfer */
    if ( eflag == ON ) {
      rs_puts( msg );
      crlf();
    }
  }
  /* command interpreter */
  if ( uflag == ON ) {
    /* clear event flag */
    uflag = OFF ;
    /* new line */
    crlf();
    /* get command */
    cmd = *(sbuf+0) ;
    /* help */
    if ( cmd == '?' ) { show_help() ; }
    /* execute */
    if ( cmd == 'E' ) { eflag = ON ; }
    /* idle */
    if ( cmd == 'I' ) { eflag = OFF ; }
    /* show status */
    if ( cmd == 'S' ) {
      /* message */
      if ( eflag == ON ) { rs_puts("run"); }
      else               { rs_puts("idle"); }
      /* new line */
      crlf();
    }
  }
}

/* receive interrupt */
void serialEvent()
{
  char ch;

  if ( Serial.available() > 0 ) {
    /* get 1 character */
    ch = Serial.read();
    /* store */
    *(sbuf+sindex) = ch ;
    /* increment */
    sindex++ ;
    /* judge */
    if ( ch == '\r' ) {
      sindex = 0 ; 
      uflag  = ON ;
    }
  }
}

char get_asc(byte x)
{
  byte result ;
  /* default */
  result = '0' ;
  /* judge */
  if ( x < 10 ) { result = x + '0' ; }
  if ( 9 < x && x < 16 ) { result = x - 10 + 'A' ; }

  return result ;
}

void show_help()
{
  rs_puts("? help");        crlf();
  rs_puts("E execute");     crlf();
  rs_puts("I idle");        crlf();
  rs_puts("S show status"); crlf();
}

void rs_putchar(char x)
{
  Serial.write(x);
}

void rs_puts(char *ptr)
{
  while ( *ptr ) {
    rs_putchar( *ptr );
    ptr++;
  }
}

void crlf()
{
  rs_putchar('\r');
  rs_putchar('\n');
}

void  send_led(byte x)
{
  if ( x ) { PORTB |=  (1 << LED_BIT) ; }
  else     { PORTB &= ~(1 << LED_BIT) ; }
}

void  update_trigger(void)
{
  /* led handling */
  send_led(xcnt & ON);
  /* set flag */
  aflag = ON ;
  /* update counter */
  xcnt++ ;
  /* judge */
  if ( xcnt == LASTXCNT ) {
    xcnt = 0 ;
    tflag = ON ;
  }
}

byte pre_rev(byte x)
{
  byte result ;
  /* default */
  result = 0 ;
  /* judge */
  if ( x & 8 ) { result |= 1 ; }
  if ( x & 4 ) { result |= 2 ; }
  if ( x & 2 ) { result |= 4 ; }
  if ( x & 1 ) { result |= 8 ; }
  
  return result ;
}

byte rev(byte x)
{
  byte dh ;
  byte dl ;
  /* reverse */
  dh = pre_rev( (x >> 4) & MASK0F ) ;
  dl = pre_rev( x & MASK0F ) ;
  
  return( (dh << 4) | dl ) ;
}

 要点だけを説明します。

 2つのDIPスイッチ状態入力に、タイマー割込みを利用。

 MsTimer2を利用して、状態入力のタイミングを
 フラグで通知して貰います。

 loopの中に、タイマー割込みのイベントフラグを扱う
 ブロックを用意します。

  if ( aflag == ON ) {
    /* clear event flag */
    aflag = OFF ;
    /* get value */
    /* convert value */
    /* update state */
  }

 Processingへ、その情報を送信するかしないかは
 別のプロセスに任せるとして、スイッチ情報入力
 だけで仕事を完結させます。

 現在のスイッチ状態を、マルチプレクサを利用して
 1ビットごとに入力し、変数に保存します。その値
 のコピーを作っておき、Processingから要求がある
 ときに、送信することに。

 次のようにコードを書きました。

  if ( aflag == ON ) {
    /* clear event flag */
    aflag = OFF ;
    /* default */
    tmpa = 0 ;
    tmpb = 0 ;
    /* get value */
    for ( int i = 0 ; i < 8 ; i++ ) {
      /* impress address */
      PORTC = (i & 7) ;
      /* delay */
      delayMicroseconds(10);
      /* get code */
      if ( PINC & 0x08 ) { tmpa |= ON ; }
      if ( PINC & 0x10 ) { tmpb |= ON ; }
      /* shift */
      if ( i < 7 ) {
        tmpa <<= 1 ;
        tmpb <<= 1 ;
      }
    }
    /* store */
    *(xswv+0) = rev( tmpa );
    *(xswv+1) = rev( tmpb );

 スイッチ基板をポートC(D14からD18)に接続。

 ポートCは、アナログポートとして使えますが
 デジタルポートで利用。

 マルチプレクサに対して、どのビットを必要と
 するのかを指定し、左右のDIPスイッチ状態を
 逐次取得。

 人間の操作は、10msよりもはるかに長いので
 スイッチ状態を逐次取得しても、問題なしと
 考えました。

 Processingとはシリアルインタフェースでの情報
 交換が最も楽なので、コマンドインタプリタ利用
 とします。

 コマンドは、以下としました。

 コマンドインタプリタは、以下。

  if ( uflag == ON ) {
    /* clear event flag */
    uflag = OFF ;
    /* new line */
    crlf();
    /* get command */
    cmd = *(sbuf+0) ;
    /* help */
    if ( cmd == '?' ) { show_help() ; }
    /* execute */
    if ( cmd == 'E' ) { eflag = ON ; }
    /* idle */
    if ( cmd == 'I' ) { eflag = OFF ; }
    /* show status */
    if ( cmd == 'S' ) {
      /* message */
      if ( eflag == ON ) { rs_puts("run"); }
      else               { rs_puts("idle"); }
      /* new line */
      crlf();
    }
  }

 関数loopの中のシーケンスを定義後、より下のレベルでの
 動作関数を定義していき、スケッチを完成させました。


目次

inserted by FC2 system