はじめに

 

C言語を少しかじったが、やめてしまった皆様。

 

if、for、while、switch...

大体のC(++)本で学んだことがある方もいらっしゃるっと思う。

 

私もそうだった。コマンド上のHello Worldが精いっぱいだった。

その後Arduino開発で生きることになるのですがそれはまた別の話。

 

そんな時

 

友人「ゲーム作ってみないか???

といわれ恐る恐る手を出した

 

それがDXライブラリである

 

そんなきっかけで触り始めると

サクサクゲームが作れたのだ!

 

というわけで基礎を学んでいればゲームが作れるアドオンを紹介したいと思う。

 

iOSアプリやAndroidアプリも開発できるが

今回はWindowsでの開発を進めていくことにする

 

DXライブラリ Windows版の説明

DXライブラリ Windows版は、DirectXを使ったWindowsデスクトップアプリの開発に必ず付いて回るDirectXやWindows関連のプログラムを使い易くまとめた形で利用できるようにしたC++言語用のゲームライブラリです。(使用する際はC言語の知識だけで大丈夫です)
これによってプログラマーはゲームの本質的なプログラムに専念することが出来ます。かなり本格的なソフト制作からお遊び程度のミニゲーム制作まで幅広くカバーしています!

プログラムソースも公開していますので気兼ねなく使用してください。

 

引用元:

 

 

  使用するソフト

 

Visual Studio 2022

 

DXライブラリー

https://dxlib.xsrv.jp/DxLib/DxLib_VC3_23.zip

 

次回インストール方法をざっくり紹介していきます!

 

 

  私が勉強につかったプログラミング入門書

 

 

 

 

 

 

 

 

 

AD

ちょっと前に作ったM5Stack GPSラップタイマー

 

 

今回は前回から

物理的な改造は無しでアップグレードできます


 使ったもの

 

  

 

前回同様、

接続はNEO-6Mとシリアル2でRX→TX、TX→RXと互い違いに接続すればOK

ドライバー、ライブラリーなどのインストールは別途必要です

 

で、何が変わったの?

前回作ったのが↓の写真

実はサーキットでV1を使用した際に分かりずらかったので

改善しようというのが今回の回です。

 

1つめはSDカードへの記録(v2,v3共通)

電源が切れるとラップが分からないのでmicroSDカードに保存できるように改善

 

1.microSDカードを挿してラップを記録するとcsvファイルが作られます

 

2.ラップ数、タイム、最高時速、日時時刻が記録されます

 

3.Excelやスプレッドシートで簡単に読み込め、グラフも作成可能

 

家に帰ってからも見返すことが可能です

 

そしてお次はUIの改善

 

V2.0は過去のラップがその場で確認可能

 

V3,0は走行中に欲しい情報が一目で分かる

ことを意識しています

 

★注意★

私の使っているNEO-6M(GPSモジュール)は

u-centerで

ボーレート9600bps→115200bps

更新頻度→1000ms(1Hz)→100ms(10Hz)

に変更しています

これを行わないと記録できるラップ時間が1秒単位になり正確な記録ができません

(とはいっても設定変更しても1/10秒までです)

u-centerでの設定変更、書き込み方法はググってください

3.0 スケッチ

#include <M5Stack.h>
#include <TinyGPS++.h>
TinyGPSPlus gps;

File file;
// 保存するファイル名
String fname = "/LAP_log.csv";

int YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, LapCount, SatVal, BestLapNum;


//LAT0、LONG0は起動時の初期原点です(サンプルはFSWカートコースです)

//左のボタンで原点を現在位置に設定可能
floatLAT0 = 35.3698692322 , LONG0 = 138.9336547852, LAT, LONG, KMPH, TopSpeed, ALTITUDE, distanceToMeter0, BeforeTime, LAP, LAP1, LAP2, LAP3, LAP4, LAP5, BestLap = 99999, AverageLap, Sprit;
boolean LAPCOUNTNOW, LAPRADchange;

float LAPRAD = 5;//ラップ計測のトリガー(原点からの半径)距離

long lastdulation;

void setup()
{
SD.begin();
Serial.begin(115200);

//GPSモジュールのボーレートに合わせてください(購入時は多分9600bps)
Serial2.begin(115200);
M5.begin();
M5.Speaker.end();
M5.Lcd.setBrightness(255);
M5.Lcd.setTextColor(WHITE);
M5.Lcd.setTextSize(1);
M5.Lcd.setCursor(10, 10);
M5.Lcd.print("Start");
LapCount = 0;

file = SD.open(fname, FILE_APPEND);
file.println("LAPCount,LapTime,TopSpeed,YYYY/MM/DD/Hour:Minute:Second");
file.close();
}

void loop()
{
//**********GPSから値を読み込み代入
ReadGPS();

//**********条件によるラップ計測
CountLAP();

//**********M5Stackへ描画
showvalue(1000);
}

void ReadGPS()
{
//**********GPS形式をライブラリで扱えるよう変換
while (Serial2.available()) {
char c = Serial2.read();
gps.encode(c);
Serial.print(c);
}
//**********GPSデーターをもとに各値へ変換
LAT = gps.location.lat();
LONG = gps.location.lng();
YEAR = gps.date.year();
MONTH = gps.date.month();
DAY = gps.date.day();
HOUR = gps.time.hour();
MINUTE = gps.time.minute();
SECOND = gps.time.second();
KMPH = gps.speed.kmph();
ALTITUDE = gps.altitude.meters() ;
distanceToMeter0 = gps.distanceBetween(gps.location.lat(), gps.location.lng(), LAT0, LONG0) ;
SatVal = gps.satellites.value();

//**********ラップ計測中の最高速度を代入
if (TopSpeed < KMPH)
{
TopSpeed = KMPH;
}


//**********日本標準時(JST)へ変換
HOUR += 9;
if (HOUR >= 24)
{
DAY += HOUR / 24;
HOUR = HOUR % 24;
}


//**********相対距離原点設定
M5.update();
if (M5.BtnA.isPressed())
{
LAT0 = LAT;
LONG0 = LONG;
distanceToMeter0 = gps.distanceBetween(gps.location.lat(), gps.location.lng(), LAT0, LONG0) ;
}

if (M5.BtnB.isPressed() == false && LAPRADchange == true)
{
LAPRADchange = false;
}

if (M5.BtnB.isPressed() == true && LAPRADchange == false)
{
if (LAPRAD == 50)
{
LAPRAD = 0;
}
LAPRAD += 5;
LAPRADchange = true;
//delay(200);
}
}

void CountLAP()
{

if ( distanceToMeter0 >= LAPRAD && M5.BtnC.isPressed() == false && LAPCOUNTNOW == true)
{
LAPCOUNTNOW = false;
}

//**********4ラップ計測
if ((( distanceToMeter0 != 0 && distanceToMeter0 <= LAPRAD ) || M5.BtnC.isPressed() == true) && LAPCOUNTNOW == false && ((millis() - BeforeTime) / 1000) > 10)
{
if (LapCount > 0)
{
LAP5 = LAP4;
LAP4 = LAP3;
LAP3 = LAP2;
LAP2 = LAP1;
LAP1 = LAP;

LAP = (millis() - BeforeTime) / 1000;
BeforeTime = millis();
if (LAP < BestLap) {
BestLap = LAP;
BestLapNum = LapCount + 1;
}
writeData();

Sprit += LAP;
if (LapCount > 1)
{
AverageLap = Sprit / LapCount;
}
} else {
BeforeTime = millis();
}

LapCount++;
LAPCOUNTNOW = true;
}
}

void showvalue(int dulation) {
if (millis() > lastdulation + dulation)
{
lastdulation = millis();
//**********ボタン説明
M5.Lcd.clear();
M5.Lcd.setTextColor(ORANGE);
M5.Lcd.setTextSize(1);

M5.Lcd.setCursor(15, 228);
M5.Lcd.setTextColor(ORANGE);
M5.Lcd.print("SET ");
M5.Lcd.setTextColor(CYAN);
M5.Lcd.print("Zero");
M5.Lcd.setTextColor(ORANGE);
M5.Lcd.print("-Point");

M5.Lcd.setTextColor(ORANGE);
M5.Lcd.setCursor(120, 228);
M5.Lcd.print("Rad= ");
M5.Lcd.setTextColor(47072);
M5.Lcd.print(LAPRAD);
M5.Lcd.print(" m");

M5.Lcd.setTextColor(ORANGE);
M5.Lcd.setCursor(230, 228);
M5.Lcd.print("Lap Count");

//**********時刻表示
M5.Lcd.setTextColor(WHITE);
M5.Lcd.setTextSize(2);
M5.Lcd.setCursor(5, 0);
M5.Lcd.print(YEAR);
M5.Lcd.print("/");
M5.Lcd.print(MONTH);
M5.Lcd.print("/");
M5.Lcd.print(DAY);
M5.Lcd.print(" ");
M5.Lcd.print(HOUR);
M5.Lcd.print(":");
M5.Lcd.print(MINUTE);
M5.Lcd.print(":");
M5.Lcd.println(SECOND);

//**********衛星数表示
M5.Lcd.setTextColor(CYAN);
M5.Lcd.setTextSize(1);
M5.Lcd.setCursor(245, 5);
M5.Lcd.print("G P S:");
M5.Lcd.setTextSize(2);
M5.Lcd.setCursor(285, 1);
M5.Lcd.print(SatVal);

//**********前ラップ表示
M5.Lcd.fillRect(0, 20, 320, 59, YELLOW); //黄色ベタ表示
M5.Lcd.setTextColor(BLACK);
M5.Lcd.setTextSize(3);
M5.Lcd.setCursor(15, 30);
M5.Lcd.print(LapCount);
M5.Lcd.print(">");
M5.Lcd.setTextSize(6);
M5.Lcd.print(LAP, 3);

/*
//**********2コ前ラップ表示
if (LapCount > 1)
{
M5.Lcd.setTextColor(WHITE);
M5.Lcd.setTextSize(3);
M5.Lcd.setCursor(140, 175);

M5.Lcd.print(LapCount - 1);
M5.Lcd.print(">");
M5.Lcd.println(LAP1, 3);
}
*/

//**********タイム差表示
//M5.Lcd.drawRect(0, 80, 170, 50, WHITE); //枠だけ

if (LAP - LAP1 <= 0)
{
M5.Lcd.fillRect(1, 80, 178, 50, BLUE);
}
if (LAP - LAP1 > 0)
{
M5.Lcd.fillRect(1, 80, 178, 50, RED);
}

M5.Lcd.setTextColor(BLACK);
M5.Lcd.setCursor(10, 92);
M5.Lcd.setTextSize(4);
if (LAP - LAP1 > 0)
{
M5.Lcd.print("+");
}
M5.Lcd.print(LAP - LAP1, 1);

M5.Lcd.setTextColor(WHITE);
M5.Lcd.setCursor(8, 90);
M5.Lcd.setTextSize(4);
if (LAP - LAP1 > 0)
{
M5.Lcd.print("+");
}
M5.Lcd.print(LAP - LAP1, 1);


//**********計測時間表示
M5.Lcd.drawRoundRect(180, 80, 140, 50, 10 , WHITE); //枠だけ

M5.Lcd.setTextColor(WHITE);
M5.Lcd.setCursor(190, 90);
M5.Lcd.setTextSize(4);
M5.Lcd.print((millis() - BeforeTime) / 1000, 0);

M5.Lcd.setTextSize(2);
M5.Lcd.setCursor(300, 110);
M5.Lcd.print("s");

//**********ベストラップ表示
M5.Lcd.setTextColor(GREEN);
M5.Lcd.setTextSize(2);
M5.Lcd.setCursor(20, 145);
M5.Lcd.print("Best(");
M5.Lcd.print(BestLapNum);
M5.Lcd.print(")");
if (BestLap != 99999)
{
M5.Lcd.setCursor(120, 140);
M5.Lcd.setTextSize(3);
M5.Lcd.print("> ");
M5.Lcd.print(BestLap);
}

//**********平均タイム表示
M5.Lcd.setTextColor(CYAN);
M5.Lcd.setTextSize(2);
M5.Lcd.setCursor(20, 175);
M5.Lcd.print("Average");
if (AverageLap != 0)
{
M5.Lcd.setCursor(120, 170);
M5.Lcd.setTextSize(3);
M5.Lcd.print("> ");
M5.Lcd.print(AverageLap);
}

//**********平均ラップとの差表示
M5.Lcd.fillRect(10, 200, 300 * ((AverageLap - (millis() - BeforeTime) / 1000) / AverageLap), 25, PINK);
M5.Lcd.drawRect(10, 200, 300, 25, WHITE); //枠だけ


//**********時速表示
M5.Lcd.setTextColor(WHITE);
M5.Lcd.setCursor(20, 205);
M5.Lcd.setTextSize(2);
M5.Lcd.print(KMPH, 1);
M5.Lcd.setTextSize(2);
M5.Lcd.print(" km/h");

//**********原点との相対距離表示
M5.Lcd.setTextColor(WHITE);
M5.Lcd.setTextSize(1);
//M5.Lcd.setCursor(140, 205);
//M5.Lcd.print("Distance=");
M5.Lcd.setCursor(160, 205);
M5.Lcd.setTextSize(2);
M5.Lcd.print(distanceToMeter0, 1);
M5.Lcd.print(" m");
}
}

void writeData() {
// SDカードへの書き込み処理(ファイル追加モード)
file = SD.open(fname , FILE_APPEND);

file.print((String)LapCount + ",");
file.print((String)LAP + ",");
file.print((String)TopSpeed + ",");
file.println((String)YEAR + "/" + (String)MONTH + "/" + (String)DAY + "-" + (String)HOUR + ":" + (String)MINUTE + ":" + (String)SECOND + ",");
file.close();
TopSpeed = 0; //最高速度をリセット
}

 

V2.0 スケッチ

#include <M5Stack.h>
#include <TinyGPS++.h>
TinyGPSPlus gps;

File file;
// 保存するファイル名
String fname = "/LAP_log.csv";

char *eventName[] = { "", "ボタンB", "ボタンC" };
int YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, LapCount, SatVal;

float LAT0 = 35.3698692322 , LONG0 = 138.9336547852, LAT, LONG, KMPH, TopSpeed, ALTITUDE, distanceToMeter0, BeforeTime, LAP, LAP1, LAP2, LAP3, LAP4, LAP5, BestLap = 9999;
boolean LAPCOUNTNOW, LAPRADchange;

float LAPRAD = 5;//ラップ計測のトリガー(原点からの半径)距離

void setup()
{
SD.begin();
Serial.begin(115200);
Serial2.begin(115200);
M5.begin();
M5.Speaker.end();
M5.Lcd.setBrightness(255);
M5.Lcd.setTextColor(WHITE);
M5.Lcd.setTextSize(1);
M5.Lcd.setCursor(10, 10);
M5.Lcd.print("Start");
LapCount = 0;

file = SD.open(fname, FILE_APPEND);
file.println("LAPCount,LapTime,TopSpeed,YYYY/MM/DD/Hour:Minute:Second");
file.close();
}

void loop()
{
//**********GPSから値を読み込み代入
ReadGPS();

//**********条件によるラップ計測
CountLAP();

//**********M5Stackへ描画
showvalue(1000);
}

void ReadGPS()
{
//**********GPS形式をライブラリで扱えるよう変換
while (Serial2.available()) {
char c = Serial2.read();
gps.encode(c);
Serial.print(c);
}
//**********GPSデーターをもとに各値へ変換
LAT = gps.location.lat();
LONG = gps.location.lng();
YEAR = gps.date.year();
MONTH = gps.date.month();
DAY = gps.date.day();
HOUR = gps.time.hour();
MINUTE = gps.time.minute();
SECOND = gps.time.second();
KMPH = gps.speed.kmph();
ALTITUDE = gps.altitude.meters() ;
distanceToMeter0 = gps.distanceBetween(gps.location.lat(), gps.location.lng(), LAT0, LONG0) ;
SatVal = gps.satellites.value();

//**********ラップ計測中の最高速度を代入
if (TopSpeed < KMPH)
{
TopSpeed = KMPH;
}

//**********日本標準時(JST)へ変換
HOUR += 9;
if (HOUR >= 24)
{
DAY += HOUR / 24;
HOUR = HOUR % 24;
}


//**********相対距離原点設定
M5.update();
if (M5.BtnA.isPressed())
{
LAT0 = LAT;
LONG0 = LONG;
distanceToMeter0 = gps.distanceBetween(gps.location.lat(), gps.location.lng(), LAT0, LONG0) ;
}

if (M5.BtnB.isPressed() == false && LAPRADchange == true)
{
LAPRADchange = false;
}

if (M5.BtnB.isPressed() == true && LAPRADchange == false)
{
if (LAPRAD == 50)
{
LAPRAD = 0;
}
LAPRAD += 5;
LAPRADchange = true;
//delay(200);
}
}

void CountLAP()
{

if ( distanceToMeter0 >= LAPRAD && M5.BtnC.isPressed() == false && LAPCOUNTNOW == true)
{
LAPCOUNTNOW = false;
}

//**********4ラップ計測
if ((( distanceToMeter0 != 0 && distanceToMeter0 <= LAPRAD ) || M5.BtnC.isPressed() == true) && LAPCOUNTNOW == false )
{

LAP5 = LAP4;
LAP4 = LAP3;
LAP3 = LAP2;
LAP2 = LAP1;
LAP1 = LAP;

LAP = (millis() - BeforeTime) / 1000;
BeforeTime = millis();
if (LAP < BestLap && LAP > 10) {
BestLap = LAP;
}
writeData();
//delay(200);
LapCount++;
LAPCOUNTNOW = true;
}
}

void showvalue(int dulation) {
if (millis() % dulation == 0)
{
//**********ボタン説明
M5.Lcd.clear();
M5.Lcd.setTextColor(ORANGE);
M5.Lcd.setTextSize(1);

M5.Lcd.setCursor(15, 228);
M5.Lcd.setTextColor(ORANGE);
M5.Lcd.print("SET ");
M5.Lcd.setTextColor(CYAN);
M5.Lcd.print("Zero");
M5.Lcd.setTextColor(ORANGE);
M5.Lcd.print("-Point");

M5.Lcd.setTextColor(ORANGE);
M5.Lcd.setCursor(120, 228);
M5.Lcd.print("Rad= ");
M5.Lcd.setTextColor(47072);
M5.Lcd.print(LAPRAD);
M5.Lcd.print(" m");

M5.Lcd.setTextColor(ORANGE);
M5.Lcd.setCursor(230, 228);
M5.Lcd.print("Lap Count");

//**********時刻表示
M5.Lcd.setTextColor(WHITE);
M5.Lcd.setTextSize(2);
M5.Lcd.setCursor(5, 0);
M5.Lcd.print(YEAR);
M5.Lcd.print("/");
M5.Lcd.print(MONTH);
M5.Lcd.print("/");
M5.Lcd.print(DAY);
M5.Lcd.print(" ");
M5.Lcd.print(HOUR);
M5.Lcd.print(":");
M5.Lcd.print(MINUTE);
M5.Lcd.print(":");
M5.Lcd.println(SECOND);

//**********衛星数表示
M5.Lcd.setTextColor(CYAN);
M5.Lcd.setTextSize(1);
M5.Lcd.setCursor(250, 5);
M5.Lcd.print("G P S:");
M5.Lcd.setTextSize(2);
M5.Lcd.setCursor(290, 1);
M5.Lcd.print(SatVal);

//**********黄色ベタ表示
M5.Lcd.fillRect(10, 20, 300, 180, YELLOW);

//**********経過時間表示
M5.Lcd.drawRect(195, 115, 110, 80, BLACK); //枠だけ
M5.Lcd.setTextColor(BLACK);
M5.Lcd.setTextSize(3);
M5.Lcd.setCursor(230, 120);
M5.Lcd.print("NOW");
M5.Lcd.setCursor(200, 150);
M5.Lcd.setTextSize(5);
M5.Lcd.print((millis() - BeforeTime) / 1000, 0);
M5.Lcd.print("s");

//**********ラップ履歴表示
if (LapCount > 0)
{
M5.Lcd.setTextColor(BLACK);
M5.Lcd.setTextSize(3);
M5.Lcd.setCursor(0, 80);
M5.Lcd.print(" ");
M5.Lcd.print(LapCount);
M5.Lcd.print(">");
M5.Lcd.setCursor(90, 70);
M5.Lcd.setTextSize(5);
M5.Lcd.print(LAP, 3);
}

M5.Lcd.setTextColor(BLACK);
M5.Lcd.setTextSize(3);
M5.Lcd.setCursor(0, 115);

if (LapCount > 1)
{
M5.Lcd.print(" ");
M5.Lcd.print(LapCount - 1);
M5.Lcd.print(">");
M5.Lcd.println(LAP1, 3);
}

if (LapCount > 2)
{
M5.Lcd.print(" ");
M5.Lcd.print(LapCount - 2);
M5.Lcd.print(">");
M5.Lcd.println(LAP2, 3);
}

if (LapCount > 3)
{
M5.Lcd.print(" ");
M5.Lcd.print(LapCount - 3);
M5.Lcd.print(">");
M5.Lcd.println(LAP3, 3);
}


M5.Lcd.setTextColor(BLACK);
M5.Lcd.setTextSize(3);
M5.Lcd.setCursor(20, 40);
M5.Lcd.print("Best:");
M5.Lcd.setCursor(110, 30);
M5.Lcd.setTextSize(5);
M5.Lcd.print(BestLap);
/*
//**********緯度表示
M5.Lcd.setTextColor(WHITE);
M5.Lcd.setTextSize(1);
M5.Lcd.setCursor(30, 175);
M5.Lcd.print("LAT= ");
M5.Lcd.println(LAT, 10);

//**********経度表示
M5.Lcd.setTextColor(WHITE);
M5.Lcd.setTextSize(1);
M5.Lcd.setCursor(180, 175);
M5.Lcd.print("LONG=");
M5.Lcd.println(LONG, 10);

//**********緯度原点表示
M5.Lcd.setTextColor(WHITE);
M5.Lcd.setTextSize(1);
M5.Lcd.setCursor(30, 190);
M5.Lcd.print("LAT0=");
M5.Lcd.println(LAT0, 10);

//**********経度原点表示
M5.Lcd.setTextColor(WHITE);
M5.Lcd.setTextSize(1);
M5.Lcd.setCursor(180, 190);
M5.Lcd.print("LONG0=");
M5.Lcd.println(LONG0, 10);
*/

//**********時速表示
M5.Lcd.setTextColor(WHITE);
M5.Lcd.setCursor(20, 205);
M5.Lcd.setTextSize(2);
M5.Lcd.print(KMPH, 1);
M5.Lcd.setTextSize(2);
M5.Lcd.print(" km/h");

//**********原点との相対距離表示
M5.Lcd.setTextColor(WHITE);
M5.Lcd.setTextSize(1);
M5.Lcd.setCursor(140, 205);
M5.Lcd.print("Distance=");
M5.Lcd.setTextSize(2);
M5.Lcd.print(distanceToMeter0, 1);
M5.Lcd.print(" m");
}
}

void writeData() {
// SDカードへの書き込み処理(ファイル追加モード)
file = SD.open(fname , FILE_APPEND);

file.print((String)LapCount + ",");
file.print((String)LAP + ",");
file.print((String)TopSpeed + ",");
file.println((String)YEAR + "/" + (String)MONTH + "/" + (String)DAY + "-" + (String)HOUR + ":" + (String)MINUTE + ":" + (String)SECOND + ",");
file.close();
TopSpeed = 0; //最高速度をリセット
}

 

改善したい点

  • 設定した原点等の設定をEEPROMに書き込み次回起動時に再開できるようにしたい
  • ショートコースに入る時や休憩後に再開すると平均タイムが狂うので直近の平均(アベレージ)タイムを表示させたい
  • Bluetoothでラップタイムなどをリアルタイムにスマホ等へ送り表示させたい
AD

この記事を読めば

前後左右に進むラジコンカー

が作れます!

 

【お詫び】記事再編集中にデザインが崩れてしまったので質素なつくりになっております

 

 

今回使用したもの

 

(代用しているのもあります)

 

  • ダイソーのラジコン

(今回はオフロードタイプをチョイス)

 

 

※重要※

技適に通っていないため日本で使用しないことを推奨します

(海外製の一部スマホもついてなかったりするけど普通に使われているみたい)

 

  • 電池(今回は9Vタイプ)

 

+その他工具など 

 

作業手順

 

あらかじめHC-05同士をペアリングしておく

 

 

  1. ラジコン本体を分解し、モーター二つを使えるようにする
  2. 配線をする
  3. Arduinoにスケッチを書き込む
  4. 動作確認
  5. 本組み
  6. 魔改造完了?

受信側(ラジコン本体)配線

前: D4---ボタン---GND

後: D5---ボタン---GND

左: D6---ボタン---GND

右: D7---ボタン---GND

今回プルアップのため、ピンをグラウンドに落とすと1→0に反応します

受信側(ラジコン本体)配線図

わかりやすく書いたつもりです

 

Arduino電源はモータードライバーの+5VをVINにつないで動かしています

 

ArduinoとHC-05はソフトウェアシリアル通信でつないでいます

 

モータードライバーを介することでモーターの正転、逆転が容易に行えますが9V電池につなぐとモーターに5V程流れますので注意

 

スケッチの中にあるデーターの数字は

0=停止

1=前進

2=後進

3=左

4=右

5=左前進

6=右前進

7=左後進

8=右後進

を意味しています

スケッチは最後のほうに掲載しています!

 

 

改造の様子

 

こちらが買ってきたラジコン550円也

 

本体の中身はいたってシンプル

 

こちら受信機もやはりシンプルだった

今回ボタンは活用したいところ

 

いきなりだがハンドル(舵)をになうモーターを外す

 

前のモーターを外すとこんな感じ

ピニオンギアで前輪の軸をずらすことでタイヤが曲がる仕組みだ

660円のスポーツカータイプラジコンと同じ構造のようだ

 

なかはスカスカなので、ある程度基板は押し込めそうだ

 

こちらが配線している様子。

今回はArduinoの小型互換機を使用している

 

仮組の様子

一応走ったのだがちょっとタイムラグがあるなぁ

 

 

送信側

#include <SoftwareSerial.h>
SoftwareSerial bt(10, 11);

#define drive_pin 4
#define back_pin 5
#define left_pin 6
#define right_pin 7

void setup() {
  bt.begin(38400);
  Serial.begin(9600);
  Serial.println("Start RC Controller");

  pinMode(drive_pin, INPUT_PULLUP);
  pinMode(back_pin, INPUT_PULLUP);
  pinMode(left_pin, INPUT_PULLUP);
  pinMode(right_pin, INPUT_PULLUP);
}

void loop() {

  if (digitalRead(drive_pin) == 1 && digitalRead(back_pin) == 1 && digitalRead(left_pin) == 1 && digitalRead(right_pin) == 1)
  {
    bt.println(0);
    Serial.println(0);
  }
  if (digitalRead(drive_pin) == 0 && digitalRead(back_pin) == 1 && digitalRead(left_pin) == 1 && digitalRead(right_pin) == 1)
  {
    bt.println(1);
    Serial.println(1);
  }
  if (digitalRead(drive_pin) == 1 && digitalRead(back_pin) == 0 && digitalRead(left_pin) == 1 && digitalRead(right_pin) == 1)
  {
    bt.println(2);
    Serial.println(2);
  }
  if (digitalRead(drive_pin) == 1 && digitalRead(back_pin) == 1 && digitalRead(left_pin) == 0 && digitalRead(right_pin) == 1 )
  {
    bt.println(3);
    Serial.println(3);
  }
  if (digitalRead(drive_pin) == 1 && digitalRead(back_pin) == 1 && digitalRead(left_pin) == 1 && digitalRead(right_pin) == 0)
  {
    bt.println(4);
    Serial.println(4);
  }
  if (digitalRead(drive_pin) == 0 && digitalRead(back_pin) == 1  && digitalRead(left_pin) == 0 && digitalRead(right_pin) == 1)
  {
    bt.println(5);
    Serial.println(5);
  }
  if (digitalRead(drive_pin) == 0 && digitalRead(back_pin) == 1 && digitalRead(left_pin) == 1 && digitalRead(right_pin) == 0)
  {
    bt.println(6);
    Serial.println(6);
  }
  if (digitalRead(drive_pin) == 1 && digitalRead(back_pin) == 0 && digitalRead(left_pin) == 0 && digitalRead(right_pin) == 1)
  {
    bt.println(7);
    Serial.println(7);
  }
  if (digitalRead(drive_pin) == 1 && digitalRead(back_pin) == 0 && digitalRead(left_pin) == 1 && digitalRead(right_pin) == 0)
  {
    bt.println(8);
    Serial.println(8);
  }
}

 

 

受信側

#include <SoftwareSerial.h>

 

 

 

SoftwareSerial bt(10, 11);

#define speed_db 2
#define speed_lr 3
#define Mr 4
#define Ml 5
#define Mb 6
#define Md 7


int pmw_db = 100;
int pmw_lr = 100;

char val;

void setup() {
  analogWrite(speed_db, pmw_db);
  analogWrite(speed_db, pmw_lr);

  bt.begin(38400);
  Serial.begin(9600);
  Serial.println("Start RC Car");

  pinMode(Md, OUTPUT);
  pinMode(Mb, OUTPUT);
  pinMode(Ml, OUTPUT);
  pinMode(Mr, OUTPUT);

  digitalWrite(Md, LOW);
  digitalWrite(Mb, LOW);
  digitalWrite(Ml, LOW);
  digitalWrite(Mr, LOW);
}
void loop() {
  while (!bt.available()) {}

  if (bt.available() > 0) {
    val = bt.read();

    switch (val) {
      case '0':
        STOP();
        break;

      case '1':
        D();
        break;

      case '2':
        B();
        break;

      case '3':
        L();
        break;

      case '4':
        R();
        break;

      case '5':
        DL();
        break;

      case '6':
        DR();
        break;

      case '7':
        BL();
        break;

      case '8':
        BR();
        break;

        /*

          4default:
          STOP();
          break;
        */

    }
  }
}


void D()
{
  digitalWrite(Md, HIGH);
  digitalWrite(Mb, LOW);
  digitalWrite(Ml, LOW);
  digitalWrite(Mr, LOW);
  Serial.println("D");
}
void B()
{
  digitalWrite(Md, LOW);
  digitalWrite(Mb, HIGH);
  digitalWrite(Ml, LOW);
  digitalWrite(Mr, LOW);
  Serial.println("B");
}
void L()
{
  digitalWrite(Md, LOW);
  digitalWrite(Mb, LOW);
  digitalWrite(Ml, HIGH);
  digitalWrite(Mr, LOW);
  Serial.println("L");
}
void  R()
{
  digitalWrite(Md, LOW);
  digitalWrite(Mb, LOW);
  digitalWrite(Ml, LOW);
  digitalWrite(Mr, HIGH);
  Serial.println("R");
}
void  DL()
{
  digitalWrite(Md, HIGH);
  digitalWrite(Mb, LOW);
  digitalWrite(Ml, HIGH);
  digitalWrite(Mr, LOW);
  Serial.println("DL");
}
void  DR()
{
  digitalWrite(Md, HIGH);
  digitalWrite(Mb, LOW);
  digitalWrite(Ml, LOW);
  digitalWrite(Mr, HIGH);
  Serial.println("DR");
}
void  BL()
{
  digitalWrite(Md, LOW);
  digitalWrite(Mb, HIGH);
  digitalWrite(Ml, HIGH);
  digitalWrite(Mr, LOW);
  Serial.println("BL");
}
void  BR()
{
  digitalWrite(Md, LOW);
  digitalWrite(Mb, HIGH);
  digitalWrite(Ml, LOW);
  digitalWrite(Mr, HIGH);
  Serial.println("BR");
}
void STOP()
{
  digitalWrite(Md, LOW);
  digitalWrite(Mb, LOW);
  digitalWrite(Ml, LOW);
  digitalWrite(Mr, LOW);
  Serial.println("STOP");
}

 

 

さいごに

 

あとはうまく配置してノーマルに近い外見に仕上げていこうと思います

送信する数字は9が一つ余っているのでライトのオンオフに割り当てることも可能です。

スケッチの改良・改造・二次配布はお断りしておりませんw

 

かなり大雑把な説明となっておりますので、ご不明な点はコメントまでお願いします!

AD

前回届いたマザーボードの原因を特定していくよ!

 

 

まずは現在の症状から

 

  • LED部分は通電する→5Vはひとまず来ている
  • ぱっと見はコンデンサーに問題なさそう

フラグ1(ヒートシンクは外していない状態で確認)

  • 電源を入れスイッチで起動してもファンは回らない

フラグ2(蛍光灯がフッと暗くなる)

  • フラグ3(CPU補助電源を抜くと起動する(画面は映らない)

 

 

24ピンのみ刺すと起動することが判明

一応電流はマザーボードに行きわたっていることが判明したので通電しないわけではない

 

 

 

で、仮起動中にCPU補助電源を刺すと!?!?

 

バチンッ!と音を立てショートします

 

ということで

CPU電源周りに問題があることが発覚

 

サーモグラフィーで起動時発熱の激しかった上部のヒートシンクを外して再度起動チェック

 

 

結果、MOS-FETの発熱が確認できました。

 

どうやらMOS-FETがショートしているが為に

電源投入後保護装置が働き起動できない

ことがわかりました。

 


壊れたMOS-FETはSIC632という型番でした

AliExpressにて部品注文したので届いたら修理します。

 

つづく

壊れたものを治す

 

これは

 

古くから続く「もったいない」精神である

 

 

 

そして時は2022年

 

とある私はメルカリでポチってしまった

 

そう

タイトルで、ばれてしまっているのだが

 

パソコンのマザーボードだ!

 

 

中古?

 

否、ジャンクじゃ(゚Д゚)

 

定価2万円が

なんと4300円

 

治せるのかは誰も知らない

 

*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*

 

 

 

届いたので開封

 

とりあえず組んでみる

 

\合体/

 

◎結論

 

起動しなかった(説明どおりでした)

 

 

_人人人人人人人人人人人人人人_
> 次回           <
>              <
>   マザーボード死す   <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^ ̄

 

次回は原因特定します

ゲーム配信作業の記録を残したいときに便利な録画方法を紹介します。

 

今回はWindowsにデフォルトで入っているXbox Game Barを使った方法となります。

 

外付けのキャプチャーボードでPCを録画する場合よりコストや手間がかからず手っ取り早いのでおすすめ

キャプチャーボードの例(PS4やSwitch、PC等の録画に便利です)

 

 

 

 

 

早速録画してみよう!

 

1,ゲームを開き

Windowsキー + G

 

2.録画を開始を押す

 

 

3.録画されていることを確認

適当なところを押す or Escキーでゲームに戻れます

 

4.録画を止める

録画を止める場合はを押すか先ほどと同じ手順を踏めばOKです。

 

5.録画ファイルを確認

ビデオ>キャプチャー の中に保存されていればOK

(設定次第で保存場所が変わる可能性があります)

Windows11(英語)の場合

Settingアプリを開く > 左から「Gaming」 > Captures 

から保存場所など設定可能

 

おつかれさまでした!

 

※録画できないときは

・ソフトがインストールされていない場合

上より入手可能です。

 

・ソフトが起動されていない場合

スタートメニューから検索して起動してください

※新たにページがリニューアル!

これから作る方はこちらが見やすくお勧め!

https://ameblo.jp/one-head/entry-12763583569.html

 

 

前回の続きです

FBXをkn5ファイルに変換しよう

ksEditorを開きます

FBXファイルを読み込みます

 

左クリック長押しで視点変更、矢印キーで移動できます

問題なければkn5(トラック)ファイルで保存します

コースが真っ黒になる場合はFBXと同じディレクトリに「texture」フォルダーを作りその中にテクスチャーファイルを入れてください

 

ゲームファイルをつくろう

フォルダーの場所を確認

Steam→ライブラリ→左側のアセコルを右クリック→プロパティ→ローカルファイル→参照

これがSteamのゲームフォルダーとなります

 

...Steam/steamapps/common/assettocorsa/content/tracks

に移動

 

ここに作成したいコースのフォルダーを作ってください

そして作成したフォルダー中に

  • ai
  • data
  • ui
  • extension

という4つのフォルダーを作成

そして先ほど作成したkn5ファイルもここに置きます

 

さらにuiフォルダーの中にテキストファイルを作成し、名前と拡張子を合わせて「ui_track.json」に変更

テキストエディターで開き以下をペーストしてください

{
    "name": "test",
    "description": "",
    "tags" : ["Japan"],
    "geotags": ["lat", "lon"],
    "country": "Japan",
    "city": "",
    "length": "?m",
    "width": "?m",
    "pitboxes": "1",
    "run": "clockwise"
}

↑のnameに表示される名前を付けられます

とりあえずこれで保存してください

 

アセットコルサを起動しましょう

ファイルをしっかり配置していればコースの選択ができます

 

さあいよいよ走れますよ!

 

上手くデーターが読み込めると無事ゲームが起動し走ることができます

 

ひとまずお疲れさまでした!

反応ががあればもう少し踏み込んだコース作成を記事にします!

※新たにページがリニューアル!

これから作る方はこちらが見やすくお勧め!

https://ameblo.jp/one-head/entry-12763583569.html

 

 

 

前回の続きです

 

今回はBlenderでコースを実際に作っていきます

まずは標準のキューブは消しましょう

 

カーブ→パスでコースのレイアウトを作ります

 

編集モードでにょきにょき伸ばし

 

最後2点を選びFキーでつなぎます

 

次に地面を作るため平面を出します

 

ループカットで適当に割ります

 

モデファイラプロパティで配列とカーブを先ほどのパスに指定するとコースが大体完成

 

メッシュに変換することでいろいろ変形することができます。

 

エンプティ→シングルアローを出します

矢印を合わせて

スタート地点→AC_START_0

ピット→AC_PIT_0

ラップタイム計測地点→AC_TIME_0_L ・ AC_TIME_0_R

 

道路には1ROADと名前を付けることで道路として使用が可能です。

 

先ほどのパスと道路にマテリアルを割り当てておきます

 

サイズを0.01に変更し、Yが上になるようにしてFBXで保存します

 

続く

 

※新たにページがリニューアル!

これから作る方はこちらが見やすくお勧め!

https://ameblo.jp/one-head/entry-12763583569.html

 

というわけで始まりました新連載!

クルマ好きでCGをやってみたいという皆様に朗報です!

自作のサーキットをCGで作って走ってみよう♪

 

独学でなんとかコースを作れたので備忘録として残しておきます

 

前提として

  • パソコンの基本的なソフトが使える・操作ができる
  • そこそこスペックが高いパソコンを所持している

方を対象としております。

 

 

 

事前準備

 

大前提となるゲーム(セールを狙いたいところ)

 

 

MOD作成ソフトウェアは基本無料です

  • Blender(無料CGソフト)

 

 

  • KsEditor(CG用(FBXデーター)→ゲーム用データー変換/もともと同梱されていますが最新版のほうが使いやすいのでいれておいたほうがいいです)

 

 

  • Content Manager(あった方が便利です)

↑のインストール方法

 
 

以下は既存のMODデーター改造時に必須となります

  • KunosSDK(ゲームファイルからCGソフト用データーに変換するため必要)

 

 

  • FBX Converter(いろいろあって上記の変換データーではそのまま読み込めないためこちらも必須)

https://www.autodesk.com/developer-network/platform-technologies/fbx-converter-archives

 

↑のインストール・使用方法

 

 

インストールお疲れ様です!

次回、ゲームファイルの作成編です!

Arduino IDE等からATMega328P-PUなどへ直接書き込む回路の組み方編です。

 

回路図

買うもの

328P-PU

 

 

CH340K

 

(変換基盤)

 

0.1μF セラコン

 

 

22pF セラコン

 

 

16MHz 水晶振動子

 

 

10KΩ抵抗

 

USBDIP基盤

 

 

そのほかブレッドボードやジャンパーワイヤーなど

 

 

接続のポイント

  • RX,TX,DTRピンが付いていることが必須条件

↑CH340Kは上記の要件を満たした安価なシリアル変換IC

  • リセットとDTRの間に0.1uF
  • V3とGNDに0.1uF

 

実際の接続写真

終わりに

誰かの役に立てば幸いです。

自作Arduino等を安く作れるのでお勧めです。

一部秋月電子通商でしか手に入らない部品がございますのでご注意ください