二足歩行ロボット動作データ作成支援4
PCプログラムの作成
ArduinoUNOからシリアルで送られてくるADCのデータを角度データに変換して、モニターに表示してBluetoothシリアルでロボットに送信する。(Bluetoothシリアルは、プログラム上からは通常のCOMポートに見える。)画面にグラフィック表示をしたい為、「DXライブラリ」を使用しています。
コンパイラは、Visual Studio Community 2019のC++を使用。2022がリリースされているのを最近知った為、2022では未検証。
メインプログラム
#include "DxLib.h"
#include "DxLib_multi_win.h" // DxLibにて別コンソールウインドウ(printf等使える。)
#include "my_serial.h" // シリアル通信
#include "srl_conf.h" // シリアル通信初期化
#include "DxLib_sub.h" // DxLibを使った描画サブルーチン
#include "rx_adc.h" // シリアル受信
////////////////////////////////
// 変数
////////////////////////////////
// 動作モード変数
bool quit_flg = false; // メインループから出るフラグ
bool robot_online = false; // ロボットとの通信モード
// 送信時間間隔計算用
int now_time = 0, last_tx_time = 0; // mS単位の時間
// 角度データ
int in_angle_data[12]; // 入力角度
int out_angle_data[12]; // 出力角度
////////////////////////////////
// データ処理関数
////////////////////////////////
// 角度データ初期化
void init_angle_data(void)
{
for (int i = 0; i < 12; i++) {
out_angle_data[i] = in_angle_data[i] = 90;
}
}
// ADCデータから角度データへ変換(0~1023 → 0~180)
void adc_to_angle(void)
{
for (int i = 0; i < 12; i++) {
in_angle_data[i] = (rx_adc_data[i] / 4) - 38;
if (in_angle_data[i] < 0) in_angle_data[i] = 0;
else if (180 < in_angle_data[i]) in_angle_data[i] = 180;
}
}
////////////////////////////////
// タスク処理関数
////////////////////////////////
// キーボードパッド入力処理タスク
// F10とF12等Visual Studioでデバックに使っている物がある。
void key_pad_task()
{
// プログラム終了?
if (key_buf[KEY_INPUT_ESCAPE]) quit_flg = true; // Escキーが押されたら終了
// ロボットのオンライン/オフラインモード処理
if (key_buf[KEY_INPUT_F1]) robot_online = true;
else if (key_buf[KEY_INPUT_F2]) robot_online = false;
// 終わり
return;
}
// 角度データ送信処理
void tx_angle_task(void)
{
char ss[80];
// オフラインなら送信しない
if (!robot_online) return;
// 送信処理
if ((now_time = GetNowCount()) >= last_tx_time + 200) { // 200mSごと
for (int i = 0; i < 12; i++) { // データ変換コピー
out_angle_data[i] = in_angle_data[i];
}
out_angle_data[0] = 180 - in_angle_data[0]; // 4個データ反転
out_angle_data[1] = 180 - in_angle_data[1];
out_angle_data[10] = 180 - in_angle_data[10];
out_angle_data[11] = 180 - in_angle_data[11];
// 送信
sprintf_s(ss, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,\n", // 文字変換ここで改行付加
out_angle_data[0], out_angle_data[1],
out_angle_data[2], out_angle_data[3],
out_angle_data[4], out_angle_data[5],
out_angle_data[6], out_angle_data[7],
out_angle_data[8], out_angle_data[9],
out_angle_data[10], out_angle_data[11]);
my_srl_fputs(ss, 1); // 文字列送信改行なし(ID1)
last_tx_time = now_time; // 送信時間保存
}
}
////////////////////////////////
// 表示処理関数
////////////////////////////////
// メーターの表示
void my_instruments_draw(void)
{
// 可変サイズメーター全画面表示テスト
int m_size = 120; // メーターサイズ
// 最大メーター表示数計算
int x_max = my_size_x / m_size; // サイズに対応したx方向の可能表示個数
int y_max = my_size_y / m_size; // サイズに対応したy方向の可能表示個数
// メーターの表示 size, y, x, *title,ofst, cw, angle_data
my_dxlib_instrument_draw(m_size, 1, 2, "LH-R", 0, true, in_angle_data[0]);
my_dxlib_instrument_draw(m_size, 2, 2, "LH-P", 0, true, in_angle_data[1]);
my_dxlib_instrument_draw(m_size, 3, 2, "LA-P", 0, true, in_angle_data[2]);
my_dxlib_instrument_draw(m_size, 4, 2, "LA-R", 0, true, in_angle_data[3]);
my_dxlib_instrument_draw(m_size, 0, 2, "LA-P", 0, true, in_angle_data[4]);
my_dxlib_instrument_draw(m_size, 0, 3, "LA-R", 0, true, in_angle_data[5]);
my_dxlib_instrument_draw(m_size, 0, 0, "RA-R", 0, true, in_angle_data[6]);
my_dxlib_instrument_draw(m_size, 0, 1, "RA-P", 0, true, in_angle_data[7]);
my_dxlib_instrument_draw(m_size, 4, 1, "RA-R", 0, true, in_angle_data[8]);
my_dxlib_instrument_draw(m_size, 3, 1, "RA-P", 0, true, in_angle_data[9]);
my_dxlib_instrument_draw(m_size, 2, 1, "RH-P", 0, true, in_angle_data[10]);
my_dxlib_instrument_draw(m_size, 1, 1, "RH-R", 0, true, in_angle_data[11]);
/*
// 可変サイズメーター全画面表示テスト
int m_size = 120;
// 最大メーター表示数計算
int x_max = my_size_x / m_size;
int y_max = my_size_y / m_size;
// 表示(表示される部分のみ)
for (int i = 0; i < y_max; i++) {
for (int j = 0; j < x_max; j++) {
my_dxlib_instrument_draw(m_size, i, j, "test", 0, true, in_angle_data[j % 12]);
}
}
*/
}
// 数値データ文字表示
void data_disp(void)
{
char ss[80];
// テキスト画面クリア(フレームバッファ)
my_dxlib_txt_flush();
// robot online/offline表示
if (robot_online) sprintf_s(ss, "robot : ONLINE");
else sprintf_s(ss, "robot : OFFLINE");
my_dxlib_txt_fputs(0, 100, ss);
// 出力角度表示
sprintf_s(ss, "output angle data"); // タイトル
my_dxlib_txt_fputs(2, 80, ss);
sprintf_s(ss, "sv00 sv01 sv02 sv03 sv04 sv05 sv06 sv07 sv08 sv09 sv10 sv11"); // サーボ名
my_dxlib_txt_fputs(3, 80, ss);
sprintf_s(ss, "LH-R LH-P LA-P LA-R LS-P LS-R RS-R RS-P RA-R RA-P RH-P RH-R"); // 関節/軸名
my_dxlib_txt_fputs(4, 80, ss);
sprintf_s(ss, " %03d %03d %03d %03d %03d %03d %03d %03d %03d %03d %03d %03d", // データ
out_angle_data[0], out_angle_data[1], out_angle_data[2], out_angle_data[3],
out_angle_data[4], out_angle_data[5], out_angle_data[6], out_angle_data[7],
out_angle_data[8], out_angle_data[9], out_angle_data[10], out_angle_data[11]);
my_dxlib_txt_fputs(5, 80, ss);
/*
// 全画面文字表示テスト
// 最大文字数計算
int x_max = my_size_x / 9;
int y_max = my_size_y / 14;
// 表示(表示される部分のみ)
for (int i = 0; i < y_max; i++) {
for (int j = 0; j < x_max; j++) {
my_dxlib_txt_fputc(i, j, '0'); // 半角キャラクタ
}
}
*/
}
// Esc, funcキー機能説明文字描画
void my_dxlib_func_keys_text_draw(void)
{
// Escキー
if (!my_dxlib_func_key_text_draw(0, "QUIT")) return; // 書けなくなったら終わり
// F1キー
if (!my_dxlib_func_key_text_draw(2, "ONLINE")) return; // 書けなくなったら終わり
// F2キー
if (!my_dxlib_func_key_text_draw(3, "OFFLINE")) return; // 書けなくなったら終わり
}
////////////////////////////////
// メイン関数
////////////////////////////////
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
// DxLib及び表示関係初期化
SetOutApplicationLogValidFlag(FALSE); // ログLog.txtを出力しない。
init_DxLib_multi_console_win(); // 別コンソールウインドウ初期化(printf使用可、DxLibウィンドウモード)
DxLib_Init(); // DXライブラリ初期化
init_my_dxlib_graph_mode(3); // 解像度設定
SetDrawScreen(DX_SCREEN_BACK); // DXライブラリー描画先を裏画面に設定
// その他初期化
if (!my_srl_open_by_file()) { // シリアル初期化
// DxLib及び表示関係解放
DxLib_End(); // DXライブラリ終了処理
close_DxLib_multi_console_win(); // 別コンソールウィンドウ解放
return 0;
}
rx_adc_task_init(); // ADC受信処理初期化
init_angle_data(); // 角度データ初期化
// スタートアップ
// アイドルループ
while (1) {
// 入力処理
get_key_pad(); // キーボードとジョイパッド状態保存
// 各種タスク
key_pad_task(); // キーボードとジョイパッド入力対応処理
rx_adc_task(); // ADC受信処理
adc_to_angle(); // ADCデータから角度データへ変換
tx_angle_task(); // 角度送信タスク
data_disp(); // robot : ONLINE/OFFLINE 表示と数値データ表示
// DxLibのメインウィンドウ表示処理
ClsDrawScreen(); // 描画画面クリア(オフスクリーンバッファー)
my_instruments_draw(); // メーターの表示
my_dxlib_txt_draw(); // 文字表示
my_dxlib_func_keys_draw(); // ファンクションキー描画
my_dxlib_func_keys_text_draw(); // ファンクションキー機能説明描画
ScreenFlip(); // 画面フリップ
// ウインドウズのメッセージ処理
if (ProcessMessage() == -1) break;
// フラグチェック
if (quit_flg) break;
}
// 解放
my_srl_close(0); // シリアルID0クローズ
my_srl_close(1); // シリアルID1クローズ
// DxLib及び表示関係解放
DxLib_End(); // DXライブラリ終了処理
close_DxLib_multi_console_win(); // 別コンソールウィンドウ解放
return 0;
}
別ウインドウでコンソールを表示させる。"DxLib_multi_win.h"
「この記事」を参照させていただき作成しています。
#if !defined(_DXLIB_MULTI_WIN_H_)
#define _DXLIB_MULTI_WIN_H_
////////////////////////////////
// 変数
////////////////////////////////
FILE* out = 0; // stdout
FILE* in = 0; // stdin
////////////////////////////////
// 初期化関数
////////////////////////////////
// DxLibにて別コンソールウインドウ初期化(コンソールにてprintf等使える。DxLibウィンドウモードも設定)
bool init_DxLib_multi_console_win(void)
{
// DXライブラリーとは別にコンソールウインドウ初期化
bool ready = AllocConsole(); // Win APIコンソール初期化
if (ready) {
freopen_s(&out, "CON", "w", stdout); // stdout
freopen_s(&in, "CON", "r", stdin); // stdin
// DXライブラリーウィンドウモード
ChangeWindowMode(TRUE); // DxLib ウィンドウモード
}
return ready;
}
// コンソール解放
bool close_DxLib_multi_console_win(void)
{
bool ready = true;
fclose(out); fclose(in); FreeConsole();
return ready;
}
#endif // _DXLIB_MULTI_WIN_H_
シリアル通信用のプログラム。"my_serial.cpp"と"my_serial.h"
「この記事」を参照させていただき作成しています。
#include <windows.h>
#include <stdio.h> // for sprintf, EOF
#include "my_serial.h"
////////////////////////////////
//マクロ
////////////////////////////////
////////////////////////////////
// 変数
////////////////////////////////
// シリアルポート関係変数
// 全体
HANDLE port_handle[PORT_ID_MAX]; // シリアルポート (HANDLEは任意型式へのポインタ)
DCB dcb[PORT_ID_MAX]; // デバイス制御ブロック (DCBは構造体)
char err_mes_str[80]; // エラーメッセージ表示用
// 初期化
// 送受信
COMSTAT ComStat[PORT_ID_MAX]; // シリアル通信の状態を受け取る構造体
////////////////////////////////
// 初期化関数
////////////////////////////////
// シリアルポート初期化
bool my_srl_open(int port_id, int com_port_num, DWORD baud, BYTE my_bytesize, BYTE my_parity, BYTE my_stopbits)
{
char com_port_str[20];
bool ready; // 汎用シリアルAPIエラーフラグ(APIが成功すると0以外。bool型なので0以外はtrue(=1)に変換される)
DWORD lastError;
// 引数エラーチェック
if (port_id >= PORT_ID_MAX) {
sprintf_s(err_mes_str, "ERR: port_id=%d\n", port_id); return false;
}
// シリアルポート初期化
sprintf_s(com_port_str, "\\\\.\\COM%d", com_port_num); // 文字列へ変換、又ポート名がCOM10以降対応の本来\\.\COMXXが特殊記号の為"\\\\.\\COMXX"になる。(COM1~9でも使える。)
port_handle[port_id] = CreateFile(com_port_str, (GENERIC_READ | GENERIC_WRITE), 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); //ポート設定
if (port_handle[port_id] == INVALID_HANDLE_VALUE) {
// エラー処理(ここではCreateFile()が失敗しているからCloseHandle()する必要は無い。)
lastError = GetLastError(); sprintf_s(err_mes_str, "CreateFile(): error=%lu, 0x%08X\n", lastError, lastError);
return false;
}
//バッファ初期化
ready = SetupComm(port_handle[port_id], 1024, 1024); // バッファ確保(ハンドル, IN, OUT)
if (!ready) {
// エラー処理
lastError = GetLastError(); sprintf_s(err_mes_str, "SetupComm(): error=%lu, 0x%08X\n", lastError, lastError);
CloseHandle(port_handle[port_id]);
return ready;
}
// 入出力バッファー破棄と未処理の読み書き処理中止
ready = PurgeComm(port_handle[port_id], PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);
if (!ready) {
// エラー処理
lastError = GetLastError(); sprintf_s(err_mes_str, "PurgeComm(): error=%lu, 0x%08X\n", lastError, lastError);
CloseHandle(port_handle[port_id]);
return ready;
}
// 指定した通信デバイスの設定をデバイス制御ブロックに格納する
GetCommState(port_handle[port_id], &dcb[port_id]); // DCB指定して通信デバイスを構成する
dcb[port_id].DCBlength = sizeof(DCB);
dcb[port_id].BaudRate = baud;
dcb[port_id].fBinary = TRUE;
dcb[port_id].ByteSize = 8;
dcb[port_id].fParity = my_parity;
dcb[port_id].StopBits = my_stopbits;
// デバイス制御ブロックに従って通信デバイスを構成する。
ready = SetCommState(port_handle[port_id], &dcb[port_id]);
if (!ready) {
// エラー処理
lastError = GetLastError(); sprintf_s(err_mes_str, "SetCommState(): error=%lu, 0x%08X\n", lastError, lastError);
CloseHandle(port_handle[port_id]);
return ready;
}
// シリアルポート初期化ここまで
sprintf_s(err_mes_str, "Srl Open: port=%s, %dbps\n", com_port_str, baud);
return ready;
}
// シリアルクローズ
bool my_srl_close(int port_id)
{
// 引数エラーチェック
if (port_id >= PORT_ID_MAX) {
sprintf_s(err_mes_str, "ERR: port_id=%d\n", port_id); return false;
}
// クローズ
CloseHandle(port_handle[port_id]);
}
////////////////////////////////
// 送受信関数
////////////////////////////////
// シリアル受信
int my_srl_fgetc(int port_id)
{
DWORD dwErrors; // シリアル通信のエラーマスクを受け取る変数
BYTE Rxdata; // 受信キャラクター(BYTEはunsigned char)
DWORD dwRead;
// 引数エラーチェック
if (port_id >= PORT_ID_MAX) {
sprintf_s(err_mes_str, "ERR: port_id=%d\n", port_id); return EOF;
}
// 受信チェック
ClearCommError(port_handle[port_id], &dwErrors, &ComStat[port_id]); // 通信エラー情報取得(受信文字数等の情報も取得)
DWORD dwCount = ComStat[port_id].cbInQue; // 受信文字数チェック
if (dwCount > 0) {
bool ready = ReadFile(port_handle[port_id], &Rxdata, sizeof(Rxdata), &dwRead, NULL); // rxdataがBYTEなので1文字受信
if (!ready) {
// こんなかんじ。加えて、FormatMessage APIを使えば説明付きで情報が得られる。
DWORD lastError = GetLastError();
sprintf_s(err_mes_str, "ReadFile(): error=%lu, 0x%08X\n", lastError, lastError);
return EOF;
}
return (int)Rxdata;
}
return EOF;
}
// シリアル送信(送信はEOFを使っていない為、charを使ってもOK)
int my_srl_fputc(BYTE ch, int port_id)
{
DWORD dwWrite;
// 引数エラーチェック
if (port_id >= PORT_ID_MAX) {
sprintf_s(err_mes_str, "ERR: port_id=%d\n", port_id); return EOF;
}
// 送信
WriteFile(port_handle[port_id], &ch, sizeof(ch), &dwWrite, NULL);
return (int)ch;
}
// 0終端を持つ文字列を送信(fputsは改行なしなのでなし)
int my_srl_fputs(char *ss, int port_id)
{
while (*ss != '\0') my_srl_fputc(*ss++, port_id);
return 0;
}
// バイナリーデータ列送信
int my_srl_bin_fputs(char *ss, int port_id, int n)
{
int i;
for (i = 1; i <= n; i++) {
my_srl_fputc(*ss++, port_id);
}
return i;
}
#if !defined(_MY_SERIAL_H_)
#define _MY_SERIAL_H_
////////////////////////////////
// マクロ
////////////////////////////////
#define PORT_ID_MAX 2
////////////////////////////////
// 外部参照変数
////////////////////////////////
extern char err_mes_str[80]; // エラーメッセージ表示用(外から書き換えてバッファーオーバーフローしない様にして下さい)
////////////////////////////////
// 関数プロトタイプ
////////////////////////////////
// シリアルポート初期化
extern bool my_srl_open(int port_id, int com_port_num, DWORD baud, BYTE my_bytesize, BYTE my_parity, BYTE my_stopbits);
// シリアルクローズ
extern bool my_srl_close(int port_id);
// シリアル受信
extern int my_srl_fgetc(int port_id);
// シリアル送信(送信はEOFを使っていない為、charを使ってもOK)
extern int my_srl_fputc(BYTE ch, int port_id);
// 0終端を持つ文字列を送信(fputsは改行なしなのでなし)
extern int my_srl_fputs(char *ss, int port_id);
// バイナリーデータ列送信
extern int my_srl_bin_fputs(char *ss, int port_id, int n);
#endif // _MY_SERIAL_H_
テキストファイルにオープンするシリアルの設定を記述してシリアルをオープンするプログラム"srl_conf.h"と設定例ファイル"serial_conf.txt"
自分の環境では、ArduinoUNOかCOM4になり、ロボットのBluetoothシリアルがCOM5に割り振られていた。
#if !defined(_SRL_CONF_H_)
#define _SRL_CONF_H_
#include <fstream> // ファイル操作関係
#include <string> // string型操作、ファイル操作に使用
#include <cstdlib> // for atoi()
// シリアル2ポートをCSVファイル(serial_conf.txt)を読んで初期化
// Visual Studioのデバック環境だとプロジェクトファイルと同じディレクリー?がカレントになる。
// Debug/ReleaseフォルダのEXEファイルを実行すると、Debug/Releaseフォルダがカレントになる。
bool my_srl_open_by_file(void)
{
// 2行x5項目x10文字 カンマ区切り
char srl_para[2][5][10];
for (int i = 0; i < 2; i++) { // 行
for (int j = 0; j < 5; j++) { // 項目
for (int k = 0; k < 10; k++) { // 文字
srl_para[i][j][k] = '\0'; // 配列初期化
}
}
}
// ファイル初期化
std::ifstream fin;
std::string filename = "serial_conf.txt";
// ファイルオープン
fin.open(filename, std::ios::in);
if (!fin) return false; // エラー
// 読込
int i, j, k; i = j = k = 0;
char ch; bool l_flg = true;
do {
if ((ch = fin.get()) == EOF) l_flg = false; // ファイル終わったら終了
else if (ch == '\n') { // 改行
i++; j = k = 0; // 行を進めて、項目と文字インデックスを0
if (i >= 2) l_flg = false; // 今回は、2行が終わったら終了
}
else if (ch == ',') { // カンマ
j++; k = 0; // 項目進めて、文字インデクスを0(仮に今回の5項目目を超えてもOKにした。改行待ち状態)
}
else if (j < 5 && k < 9) { // 文字数と項目もチェック(文字数は終端文字'\0'ぶん残す。文字数が超えても項目は変えない。カンマ待ち状態)
srl_para[i][j][k++] = ch; // 配列書込み
}
} while (l_flg);
fin.close();
// シリアル初期化
int com_port_num = atoi(srl_para[0][0]);
DWORD baud = (DWORD)atoi(srl_para[0][1]); if (baud < CBR_110 || CBR_256000 < baud) baud = CBR_9600;
BYTE bytesize = (BYTE)atoi(srl_para[0][2]); if (bytesize < 7 || 8 < bytesize) bytesize = 8;
BYTE parity = (BYTE)atoi(srl_para[0][3]); if (parity < NOPARITY || SPACEPARITY < parity) parity = NOPARITY;
BYTE stopbits = (BYTE)atoi(srl_para[0][4]); if (stopbits < ONESTOPBIT || TWOSTOPBITS < stopbits) stopbits = ONESTOPBIT;
// Debug
printf("com_port_num = %d\n", com_port_num);
printf("baud = %d\n", baud);
printf("bytesize = %d\n", bytesize);
printf("parity = %d\n", parity);
printf("stopbits = %d\n", stopbits);
my_srl_open(0, // シリアルID0初期化
com_port_num, // com port
baud, // BaudRate
bytesize, // ByteSize
parity, // Parity
stopbits); // StopBits
com_port_num = atoi(srl_para[1][0]);
baud = (DWORD)atoi(srl_para[1][1]); if (baud < CBR_110 || CBR_256000 < baud) baud = CBR_9600;
bytesize = (BYTE)atoi(srl_para[1][2]); if (bytesize < 7 || 8 < bytesize) bytesize = 8;
parity = (BYTE)atoi(srl_para[1][3]); if (parity < NOPARITY || SPACEPARITY < parity) parity = NOPARITY;
stopbits = (BYTE)atoi(srl_para[1][4]); if (stopbits < ONESTOPBIT || TWOSTOPBITS < stopbits) stopbits = ONESTOPBIT;
// Debug
printf("com_port_num = %d\n", com_port_num);
printf("baud = %d\n", baud);
printf("bytesize = %d\n", bytesize);
printf("parity = %d\n", parity);
printf("stopbits = %d\n", stopbits);
my_srl_open(1, // シリアルID1初期化
com_port_num, // com port
baud, // BaudRate
bytesize, // ByteSize
parity, // Parity
stopbits); // StopBits
// 初期化終了
return true;
}
#endif // _SRL_CONF_H_
4, 38400, 8, 0, 0, // ID0 COM4, 38400bps, 8bit data, No parity, 1 stop bit
5, 115200, 8, 0, 0, // ID1 COM5, 115200bps, 8bit data, No parity, 1 stop bit
// com port, BaudRate, ByteSize, Parity, StopBits
// 3行目以降は無視。
// Parity
// EVENPARITY:2, MARKPARITY:3, NOPARITY:0, ODDPARITY:1, SPACEPARITY:4
// StopBits
// ONESTOPBIT:0, ONE5STOPBITS:1, TWOSTOPBITS:2
// Visual Studioのデバック環境だとプロジェクトファイルと同じディレクリー?がカレントになる。
// Debug/ReleaseフォルダのEXEファイルを実行すると、Debug/Releaseフォルダがカレントになる。
DXライブラリを利用してグラフィック画面に文字やメーターやファンクションキー等を表示するプログラム"DxLib_sub.h"
#if !defined(_DXLIB_SUB_H_)
#define _DXLIB_SUB_H_
#include <cmath> // for sin, cos
#define rad(x) ((x) * 3.14159265 / 180.0) // 度からラジアン
////////////////////////////////
// 変数
////////////////////////////////
// 入力系変数
char key_buf[256]; // 全てのキーの入力状態
int pad1_in; // pad1の入力状態
// 各種表示モード
int my_dxlib_graph_mode = 0; // 解像度モード
int my_size_x = 640, my_size_y = 480; // 保存した解像度数値
// 表示バッファー
char dxlid_txt_buf[1080 / 14][(1920 / 9) + 1]; // 文字配列(最大1920x1080pixel)77行 x 213文字(+'\0')デフォルトフォント設定の半角
////////////////////////////////
// 入力系関数
////////////////////////////////
// キーボードとジョイパッドの状態を読んでおく
void get_key_pad(void)
{
GetHitKeyStateAll(key_buf);
pad1_in = GetJoypadInputState(DX_INPUT_PAD1);
return;
}
////////////////////////////////
// 画面解像度及びモード設定
////////////////////////////////
// ディスプレイの解像度以上になるとフルスクリーン表示の様になっている。(枠やタイトル等が見えない。)
// ただし、ほかのウインドウを手前に表示出来る。
int init_my_dxlib_graph_mode(int mode)
{
// 今までのモード/解像度保存
int mode_n1 = my_dxlib_graph_mode;
int last_x = my_size_x, last_y = my_size_y;
// モード確認/設定
if (0 <= mode && mode <= 5) my_dxlib_graph_mode = mode;
else my_dxlib_graph_mode = mode_n1;
// 解像度選択
my_size_x = 640; my_size_y = 480;
if (my_dxlib_graph_mode == 1) {my_size_x = 800; my_size_y = 600;}
else if (my_dxlib_graph_mode == 2) {my_size_x = 1024; my_size_y = 768;}
else if (my_dxlib_graph_mode == 3) {my_size_x = 1280; my_size_y = 720;}
else if (my_dxlib_graph_mode == 4) {my_size_x = 1280; my_size_y = 1024;}
else if (my_dxlib_graph_mode == 5) {my_size_x = 1920; my_size_y = 1080;}
// 解像度設定
int retdt = SetGraphMode(my_size_x, my_size_y, 32); // 32bitColor固定
if (retdt == DX_CHANGESCREEN_RETURN) { // 失敗戻された。
my_dxlib_graph_mode = mode_n1;
my_size_x = last_x; my_size_y = last_y;
}
else if (retdt == DX_CHANGESCREEN_DEFAULT) { // 失敗標準に設定された。
my_dxlib_graph_mode = 0;
my_size_x = 640; my_size_y = 480;
}
// ウィンドウモードタイトル
char ss[80];
sprintf_s(ss, "graph_mode = %d: Size X = %d, Size Y = %d, 32bit Color", my_dxlib_graph_mode, my_size_x, my_size_y);
SetMainWindowText(ss);
// 終わり
return retdt;
}
////////////////////////////////
// 文字表示
////////////////////////////////
void my_dxlib_txt_flush(void)
{
// 最大文字数計算
int x_max = my_size_x / 9;
int y_max = my_size_y / 14;
// バッファークリア(表示される部分のみ)
for (int i = 0; i < y_max; i++) {
for (int j = 0; j < x_max; j++) {
dxlid_txt_buf[i][j] = ' '; // スペースキャラクタ
}
dxlid_txt_buf[i][x_max] = '\0'; // 行ごとの終端
}
}
bool my_dxlib_txt_fputc(int y, int x, char ch)
{
// 最大文字数計算
int x_max = my_size_x / 9;
int y_max = my_size_y / 14;
// バッファーオーバーフロー対策
if ((y < 0 || (y_max - 1) < y) || (x < 0 || (x_max - 1) < x)) return false;
// 書込み
dxlid_txt_buf[y][x] = ch; return true;
}
// 0終端を持つ文字列を書込み(改行なし出来ない)
bool my_dxlib_txt_fputs(int y, int x, char *ss)
{
while (*ss != '\0') my_dxlib_txt_fputc(y, x++, *ss++);
return true;
}
// 画面一括描画
bool my_dxlib_txt_draw(void)
{
// 最大文字数計算
int y_max = my_size_y / 14;
// 表示(表示される部分のみ)
for (int i = 0; i < y_max; i++) {
DrawString(0, i * 14, // 文字列を書く x, y,
dxlid_txt_buf[i], // 文字列
GetColor(255, 255, 255)); // カラー
}
return true;
}
////////////////////////////////
// メーターの表示
////////////////////////////////
// 大きさ可変m_size x m_size pixel、表示場所横X(0~X-1)個x縦Y(0~Y-1)個、90度(中央)のオフセット角度、回転方向、角度(0~180)
bool my_dxlib_instrument_draw(int m_size, int y, int x, char *title, int offset, bool cw, int data)
{
// 最小30pixel~最大メーターサイズをウインドウの縦のサイズまでにする。なんとか見えるのが40pixelくらいから。
if (m_size < 30 || my_size_y < m_size) return false;
// 最大メーター表示数計算
int x_max = my_size_x / m_size;
int y_max = my_size_y / m_size;
// エラーチェック(表示場所のみ)
if ((y < 0 || (y_max - 1) < y) || (x < 0 || (x_max - 1) < x)) return false;
// 座標計算
int x0 = x * m_size, y0 = y * m_size; // 左上の座標
int x1 = (x + 1) * m_size, y1 = (y + 1) * m_size; // 右下の座標に+1
int x05 = (x * m_size) + (m_size / 2), y05 = (y * m_size) + (m_size / 2); // 中央の座標
double r1 = m_size / 3.0, r2 = m_size / 4.0; // メーター半径、目盛り内側と中心との距離
// 全体の枠
DrawBox(x0, y0, x1, y1, GetColor(255, 255, 255), FALSE);
// メーター枠の円
DrawCircle(x05, y05, (int)(r1), GetColor(255, 255, 255), FALSE);
// 目盛り
DrawLine(x05 + (int)(sin(rad(offset)) * r2), y05 - (int)(cos(rad(offset)) * r2),
x05 + (int)(sin(rad(offset)) * r1), y05 - (int)(cos(rad(offset)) * r1),
GetColor(255, 255, 255));
DrawLine(x05 + (int)(sin(rad(offset - 90)) * r2), y05 - (int)(cos(rad(offset - 90)) * r2),
x05 + (int)(sin(rad(offset - 90)) * r1), y05 - (int)(cos(rad(offset - 90)) * r1),
GetColor(255, 255, 255));
DrawLine(x05 + (int)(sin(rad(offset + 90)) * r2), y05 - (int)(cos(rad(offset + 90)) * r2),
x05 + (int)(sin(rad(offset + 90)) * r1), y05 - (int)(cos(rad(offset + 90)) * r1),
GetColor(255, 255, 255));
// 針描画
int dt = data; if (!cw) dt = (dt * -1) + 180; // 針の回転方向
DrawLine(x05, y05,
x05 + (int)(sin(rad(offset + dt - 90)) * r1), y05 - (int)(cos(rad(offset + dt - 90)) * r1),
GetColor(255, 255, 255));
// 文字描画
DrawString(x0 + 1, y0 + 1, title, GetColor(255, 255, 255)); // タイトル左上
char ss[10]; sprintf_s(ss, "%d", data);
int StrWidth = GetDrawStringWidth(ss, strlen(ss));
DrawString(x1 - (StrWidth + 1), y1 - (14 + 1), ss, GetColor(255, 255, 255)); // 角度右下
// 描画終了
return true;
}
////////////////////////////////
// ファンクションキーの表示
////////////////////////////////
// 単体の表示
bool my_dxlib_func_key_draw(int x, char *title, bool on_flg)
{
// 最大キー表示数計算
int x_max = my_size_x / 72; // 空白1+キー7で72pixel(8文字 * 9pixel)
// エラーチェック(表示場所のみ)
if (x < 0 || (x_max - 1) < x) return false;
// 座標計算
int key_x0 = (x * 72) + 9, key_y0 = ((my_size_y / 14) - 1) * 14; // キーのBOX左上
int key_x1 = (x + 1) * 72, key_y1 = (my_size_y / 14) * 14; // キーのBOX右下+1
int str_x0 = (x * 72) + 27, str_y0 = ((my_size_y / 14) - 1) * 14; // キーの文字の左上
// キーの枠描画
DrawBox(key_x0, key_y0, key_x1, key_y1, GetColor(255, 255, 255), on_flg); // キーが押されていたら塗りつぶし
// 文字描画
unsigned int str_color;
if (on_flg) str_color = GetColor(0, 0, 0); // キーが押されていたら黒抜き
else str_color = GetColor(255, 255, 255); // 押されてなければ白
DrawString(str_x0, str_y0, title, str_color);
// 描画終了
return true;
}
// ファンクションキーの機能説明
bool my_dxlib_func_key_text_draw(int x, char *title)
{
// 最大キー表示数計算
int x_max = my_size_x / 72; // 空白1+キー7で72pixel(8文字 * 9pixel)
// エラーチェック(表示場所のみ)
if (x < 0 || (x_max - 1) < x) return false;
// 座標計算
int str_x0 = (x * 72) + 9, str_y0 = ((my_size_y / 14) - 2) * 14; // キーの文字の左上
DrawString(str_x0, str_y0, title, GetColor(255, 255, 255));
// 描画終了
return true;
}
// Esc, funcキー全体描画
void my_dxlib_func_keys_draw(void)
{
// 書けなくなったら終わり
if (!my_dxlib_func_key_draw(0, "Esc", (bool)key_buf[KEY_INPUT_ESCAPE])) return;
if (!my_dxlib_func_key_draw(2, "F1", (bool)key_buf[KEY_INPUT_F1])) return;
if (!my_dxlib_func_key_draw(3, "F2", (bool)key_buf[KEY_INPUT_F2])) return;
if (!my_dxlib_func_key_draw(4, "F3", (bool)key_buf[KEY_INPUT_F3])) return;
if (!my_dxlib_func_key_draw(5, "F4", (bool)key_buf[KEY_INPUT_F4])) return;
if (!my_dxlib_func_key_draw(7, "F5", (bool)key_buf[KEY_INPUT_F5])) return;
if (!my_dxlib_func_key_draw(8, "F6", (bool)key_buf[KEY_INPUT_F6])) return;
if (!my_dxlib_func_key_draw(9, "F7", (bool)key_buf[KEY_INPUT_F7])) return;
if (!my_dxlib_func_key_draw(10, "F8", (bool)key_buf[KEY_INPUT_F8])) return;
if (!my_dxlib_func_key_draw(12, "F9", (bool)key_buf[KEY_INPUT_F9])) return;
if (!my_dxlib_func_key_draw(13, "F10", (bool)key_buf[KEY_INPUT_F10])) return;
if (!my_dxlib_func_key_draw(14, "F11", (bool)key_buf[KEY_INPUT_F11])) return;
if (!my_dxlib_func_key_draw(15, "F12", (bool)key_buf[KEY_INPUT_F12])) return;
}
#endif // _DXLIB_SUB_H_
ArduinoUNOからADコンバータのデータを受信するプログラム"rx_adc.h"
#if !defined(_RX_ADC_H_)
#define _RX_ADC_H_
#include <cctype> // for is~
#include <cstdlib> // for atoi()
////////////////////////////////
// 変数
////////////////////////////////
char rx_adc_str[16][10]; // 受信キャラクタ
int rx_adc_str_num; // 受信中チャンネル
int rx_adc_str_index; // 受信中文字数
int rx_adc_data[16]; // ADC数値データ(12個受信完了する毎にアップデート)
int rx_adc_sqc; // 受信処理実行中シーケンス
////////////////////////////////
// 関数
////////////////////////////////
// 変数初期化(文字列関係)
void rx_adc_init(void)
{
for (int i = 0; i < 16; i++) {
for (int j = 0; j < 10; j++) {
rx_adc_str[i][j] = '\0';
}
}
rx_adc_str_num = 0;
rx_adc_str_index = 0;
}
// ADC受信処理初期化(シーケンスと数値データ)
void rx_adc_task_init(void)
{
rx_adc_sqc = 0;
for (int i = 0; i < 16; i++) {
rx_adc_data[i] = 0;
}
}
// ADC受信処理(定期的に実行する。60Hzで実行する為、受信したデータは全て処理しておく)
void rx_adc_task(void)
{
int ch; // 一文字受信用バッファ
while ((ch = my_srl_fgetc(0)) != EOF) { // 一文字受信
putchar(ch); // debug
// シリアルから文字列を受信して16個分受信したら数値データに変換する
switch (rx_adc_sqc) {
case 0: // 改行待ち(前の文字列の終端)エラー及び初回のみ実行
if (ch == '\n') {
rx_adc_init(); // 文字受信バッファー初期化
rx_adc_sqc++;
}
break;
case 1: // 文字受信処理
if (ch == '\n') { // 改行?
for (int i = 0; i < 16; i++) { // データ数値化
rx_adc_data[i] = atoi(rx_adc_str[i]);
}
rx_adc_init(); // 文字受信バッファー初期化
}
else if (ch == ',') { // カンマ?
if (rx_adc_str_num >= 16) // エラー(17個目のカンマを受信)
rx_adc_sqc = 0; // シーケンス初期化
else {
rx_adc_str_num++; // ブロックを進める
rx_adc_str_index = 0; // 文字最初
}
}
else if (isdigit(ch)) { // 数字なら真
// 文字保存バッファーオーバーフロー対策入り
if ((0 <= rx_adc_str_num && rx_adc_str_num <= 15) &&
(0 <= rx_adc_str_index && rx_adc_str_index <= 8)) {
rx_adc_str[rx_adc_str_num][rx_adc_str_index++] = ch;
}
}
break;
default: // エラー
rx_adc_sqc = 0; // エラー/初期化処理へ
break;
}
}
}
#endif // _RX_ADC_H_
以上。