EV3を買いました(レコードプレイヤータイマー)

EV3を買いました。


以下の記事の内容を試す際に注意したほうが良いと思った箇所と、
記事の内容を参考に作成したレコードプレイヤータイマーのプログラムを紹介します。


レコードプレイヤータイマーなんて言っても、
「指定した時間が経過したらレコードプレイヤーの再生ボタンを押してくれる」
というだけですが。

参考にした記事で気をつけた方が良い所

5-2.ソースからのビルド

update_sdcard.shを正常に行うために、make programsの前に以下の修正が必要
("Test"を"SUBDIRS_DISABLED"から"SUBDIRS"へ移動)

*** Makefile.orig       2014-12-27 19:47:56.223963572 +0900
--- Makefile    2014-12-27 19:48:13.560049537 +0900
***************
*** 1,6 ****
  SUBDIRS = Bluetooth Brick+Datalog Brick+Info Brick+Program IR+Control \
!           Motor+Control Port+View Sleep Volume WiFi ui c_ui
! SUBDIRS_DISABLED = TEST SelfTest tst Debug Demo Test

  # Make is not friend with spaces, so we have to be creative to handle them.
  space = $(subst +,\ ,$1)
--- 1,6 ----
  SUBDIRS = Bluetooth Brick+Datalog Brick+Info Brick+Program IR+Control \
!           Motor+Control Port+View Sleep Volume WiFi ui c_ui Test
! SUBDIRS_DISABLED = TEST SelfTest tst Debug Demo

  # Make is not friend with spaces, so we have to be creative to handle them.
  space = $(subst +,\ ,$1)
7.SDカードからの起動
  • format_sdcard.shとupdate_sdcard.shはスクリプト内でsudoを使っているので、実行時にsudoは不要
    • 特にupdate_sdcard.shは「~/」を使っているのでrootで実行すると問題がある

レコードプレイヤータイマー

ほとんど「9-3 モーター制御」のとおりですが、
指定した時間が経過するとレコードプレイヤーのボタンを押してくれるアプリを作ってみました。


なお、EV3本体の組み立て型は、基本セット同封の組み立て説明書とほとんど同じです。
PortBに左車輪のモーター、PortCに右車輪のモーターが接続されています。
変更点は、前進するとボタンを押してくれるように、カブトムシみたくツノをつけた所だけです。

ソースコード

「.c」と「Makefile」の配置は記事のサンプルと同じで、
それぞれ以下のとおりです。

  • move.c
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>

#define CH1	0x01
#define CH2	0x02
#define CH3	0x04
#define CH4	0x08

/* EV3のバイトコードの定義からの抜粋です(from bytecode.h) */
typedef enum
{
	opPROGRAM_STOP		= 0x02,	// 0010
	opPROGRAM_START		= 0x03,	// 0011

	opOUTPUT_GET_TYPE	= 0xA0,	// 00000
	opOUTPUT_SET_TYPE	= 0xA1,	// 00001
	opOUTPUT_RESET		= 0xA2,	// 00010
	opOUTPUT_STOP		= 0xA3,	// 00011
	opOUTPUT_POWER		= 0xA4,	// 00100
	opOUTPUT_SPEED		= 0xA5,	// 00101
	opOUTPUT_START		= 0xA6,	// 00110
	opOUTPUT_POLARITY	= 0xA7,	// 00111
	opOUTPUT_READ		= 0xA8,	// 01000
	opOUTPUT_TEST		= 0xA9,	// 01001
	opOUTPUT_READY		= 0xAA,	// 01010
	opOUTPUT_POSITION	= 0xAB,	// 01011
	opOUTPUT_STEP_POWER	= 0xAC,	// 01100
	opOUTPUT_TIME_POWER	= 0xAD,	// 01101
	opOUTPUT_STEP_SPEED	= 0xAE,	// 01110
	opOUTPUT_TIME_SPEED	= 0xAF,	// 01111

	opOUTPUT_STEP_SYNC	= 0xB0,	// 10000
	opOUTPUT_TIME_SYNC	= 0xB1,	// 10001
	opOUTPUT_CLR_COUNT	= 0xB2,	// 10010
	opOUTPUT_GET_COUNT	= 0xB3,	// 10011

	opOUTPUT_PRG_STOP		= 0xB4,	// 10100
} OP;

int pwmfp;

int Stop(unsigned char ch)
{
	unsigned char Buf[4];
	int ret;

	Buf[0] = opOUTPUT_STOP;
	Buf[1] = ch;
	ret = write(pwmfp,Buf,2);
	return ret;
}

int PrgStart(void)
{
	unsigned char Buf[4];
	int ret;

	Buf[0] = opPROGRAM_START;
	ret = write(pwmfp,Buf,1);
	return ret;
}

int PrgStop(void)
{
	unsigned char Buf[4];
	int ret;

	Buf[0] = opPROGRAM_STOP;
	ret = write(pwmfp,Buf,1);
	return ret;
}

int Start(void)
{
	unsigned char Buf[4];
	int ret;

	Buf[0] = opOUTPUT_START;
	Buf[1] = CH1 | CH2 | CH3 | CH4;
	ret = write(pwmfp,Buf,2);
	return ret;
}

int Power(unsigned char ch, unsigned char power)
{
	unsigned char Buf[4];
	int ret;

	Buf[0] = opOUTPUT_POWER;
	Buf[1] = ch;	/* 複数のCHを指定する時は CH1 | CH2 といった形式で */
	Buf[2] = power;
	ret = write(pwmfp,Buf,3);
	return ret;
}

int Reset(unsigned char ch)
{
	unsigned char Buf[4];
	int ret;

	Buf[0] = opOUTPUT_RESET;
	Buf[1] = ch;
	ret = write(pwmfp,Buf,2);
	return ret;
}

int main(int argc, char *argv[])
{
	unsigned char power;
	unsigned int seconds;

	pwmfp = open("/dev/lms_pwm",O_RDWR);
	if (pwmfp < 0) {
		printf("Cannot open dev/lms_pwm\n");
		exit(-1);
	}

	if (argc != 3) {
		printf("Usage: %s POWER SECONDS\n", argv[0]);
		exit(-1);
	}
	power = (unsigned char)atoi(argv[1]);
	seconds = (unsigned int)atoi(argv[2]);

	PrgStop();
	PrgStart();
	Reset(CH1|CH2|CH3|CH4);

	Start();

	printf("power:%d , seconds:%d\n", (int)power, seconds);
	Power(CH2, power);
	Power(CH3, power);
	sleep(seconds);

	Power(CH2, 0);
	Power(CH3, 0);

	Stop(CH2);
	Stop(CH3);

	PrgStop();

	return 1;
}
SOURCES = move.c
SUBDIRS = move

TARGET = move

CONF = Linux
ARCH = AM1808

include ../../open_first/rules.mk
実行方法

ビルド、d_pwm.koをinsmodする所まではサンプルと同じです。


実行時に引数で以下の2つを指定します

$ ./move POWER SECONDS
  • POWER: -100 〜 100 (負値は後退)
  • SECONDS: 前進/後退 時間


以下のようなシェルスクリプトで、
「指定した時間経過後に再生」をさせることができます。

  • record_timer.sh
#!/bin/sh

sleep_min() {
	echo "sleep_min $1"
	sleep $(($1 * 60))
}

sleep_hour() {
	echo "sleep_hour $1"
	sleep_min $(($1 * 60))
}

if [ $# -ne 2 ]; then
	echo "Usage $0 HOUR MINUTES"
	exit 1
fi

sleep_hour $1
sleep_min $2

./move 10 1
./move -3 1
  • 買ったままの状態だと「cron」も「atコマンド」も使えないので、sleepコマンドを使っています


「record_timer.sh」の引数は以下のとおりです。

$ ./record_timer.sh HOURS MINUTES


以下のように実行します。

$ nohup ./record_timer.sh 6 30 &
  • 6時間30分後に再生
  • ログアウト後も継続されるよう「nohup」で実行

最後に

EV3ではLinuxで動いているので、シェルスクリプトと連携できる点が素晴らしいと思います。


モーターやセンサの入出力をコマンドとして実装しておけば、
あとは既存のLinuxコマンドと組み合わせるだけで、
案外いろんなことができてしまうんじゃないかと思います。