「技術」カテゴリーアーカイブ

【Laravel】【ダイエット支援】食事管理データベースを作成する

食事管理機能の作成に着手します。

まずはデータベースの作成。

マイグレーションを作成します。

$ php artisan make:migration create_eating_management
class CreateEatingManagement extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('eating_managements', function (Blueprint $table) {
            $table->increments('id');
            $table->date('date');
            $table->text('item');
            $table->integer('protein');
            $table->integer('ripid');
            $table->integer('carbo');
            $table->integer('calorie');
            $table->timestamps();
            $table->engine = 'InnoDB';
            $table->charset = 'utf8mb4';
            $table->collation = 'utf8mb4_unicode_ci';
        });

        Schema::create('eating_management_user', function (Blueprint $table) {
            $table->integer('user_id')
                  ->foreign('user_id')
                  ->references('id')->on('users')
                  ->onDelete('cascade');
            $table->integer('eating_management_id')
                  ->foreign('eating_management_id')
                  ->references('id')->on('eating_managements')
                  ->onDelete('cascade');
            $table->engine = 'InnoDB';
            $table->charset = 'utf8mb4';
            $table->collation = 'utf8mb4_unicode_ci';
        });

        Schema::create('timezones', function (Blueprint $table) {
            $table->increments('id');
            $table->text('zone');
            $table->timestamps();
            $table->engine = 'InnoDB';
            $table->charset = 'utf8mb4';
            $table->collation = 'utf8mb4_unicode_ci';
        });

        Schema::create('eating_management_timezone', function (Blueprint $table) {
            $table->integer('eating_management_id')
                  ->foreign('eating_management_id')
                  ->references('id')->on('eating_managements')
                  ->onDelete('cascade');
            $table->integer('timezone_id')
                  ->foreign('timezone_id')
                  ->references('id')->on('timezones')
                  ->onDelete('cascade');
            $table->engine = 'InnoDB';
            $table->charset = 'utf8mb4';
            $table->collation = 'utf8mb4_unicode_ci';
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('eating_management_timezone');
        Schema::dropIfExists('timezones');
        Schema::dropIfExists('eating_management_user');
        Schema::dropIfExists('eating_managements');
    }
}

必要な項目は、日付、時間帯、品目、タンパク質、脂質、炭水化物、カロリー。

時間帯は別テーブルで定義して、朝、昼、夜、間食から選択します。

時間帯は固定値なので、Seederで作成します。

$ php artisan make:seeder TimezoneSeeder
class TimezoneSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        DB::table('timezones')->insert(['zone' => '朝',]);
        DB::table('timezones')->insert(['zone' => '昼',]);
        DB::table('timezones')->insert(['zone' => '夜',]);
        DB::table('timezones')->insert(['zone' => '間食',]);
    }
}

これらを実行して適用させます。

$ composer dump-autoload
Generating optimized autoload files
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover
Discovered Package: fideloper/proxy
Discovered Package: laravel/tinker
Discovered Package: nesbot/carbon
Package manifest generated successfully.
$ php artisan migrate
Migrating: 2020_07_10_092328_create_eating_management
Migrated:  2020_07_10_092328_create_eating_management (0.07 seconds)
$ php artisan db:seed --class=TimezoneSeeder
Database seeding completed successfully.

作成時間にNULLって入っているけど、まあいいか。

【ラズパイ】これまでやってきたことを組み合わせる。

ここまでやってきたことを全て組み合わせます。

GLCDに表示するのは、

  • 時刻、天気、温度、湿度
  • カレンダー
  • 倉田ましろの絵

この3つの表示を、スイッチの1番を押すことで切り替えます。

import RPi.GPIO as GPIO
import time
import datetime
import calendar
import GLCD
import AM2320
import Weather
import SW


def __main__():
    GPIO.setmode(GPIO.BCM)
    GLCD.PinsInit(20, 7, 8, 9, 18, 19, 10, 11, 12, 13, 14, 15, 16, 17)
    GLCD.GLCDInit()
    GLCD.GLCDDisplayClear()

    SW.PinsInit(21, 22, 23, 24, 25, 26, 27)
    SW.Start()

    roop = 10 * 60 * 60
    mode = 1
    try:
        while True:
            key = SW.GetKey()
            if key == "1":
                GLCD.GLCDDisplayClear()
                mode += 1
                if mode > 3:
                    mode = 1

            if mode == 1:
                if roop >= 10 * 60 * 60:
                    Weather.RequestAPI()
                    weather = Weather.GetWeather()
                    temp = Weather.GetTemp()
                    roop = 0

                GLCD.GLCDPuts(1, 0, "Date :")
                GLCD.GLCDPuts(5, 8, datetime.datetime.now().strftime('%Y:%m:%d %A'))
                GLCD.GLCDPuts(1, 16, "Weather :")
                GLCD.GLCDPuts(10,24, weather)
                GLCD.GLCDPuts(10,32, "Temp : " + format(temp) + 'C')
                GLCD.GLCDPuts(1, 40, "Time : " + datetime.datetime.now().strftime('%H:%M:%S'))
                GLCD.GLCDPuts(1, 48, "Humidity    : " + AM2320.GetHum() + '%')
                GLCD.GLCDPuts(1, 56, "Temperature : " + AM2320.GetTemp() + 'C')

                roop += 1
            elif mode == 2:
                cal = calendar.month(datetime.datetime.now().year, datetime.datetime.now().month)
                cals = cal.split("\n")
                y = 0
                for c in cals:
                    GLCD.GLCDPuts(1, y, c)
                    y += 8
            
            elif mode == 3:
                GLCD.drowMashiro()

            time.sleep(0.1)
    except KeyboardInterrupt:
        SW.Stop()
        GLCD.GLCDDisplayClear()
        GPIO.cleanup()

__main__()

スイッチ押下でmodeの値が変化し、表示処理の内容を変えています。

なかなか上手くいったので、これで完成品を作ってみようか。

ラズパイ遊びの集大成です。

【C#】【ピクロス】【ALTSEED】自動テスト機能を実装する。

前回までの状況はこちら。

最新ソースはこちら。(gitHub)

https://github.com/takishita2nd/Picross

さて、いよいよ、自動テストを作っていきますか。

前回で問題データと解答データを用意したので、

問題データの読み込み→解析→回答データと照合→結果出力

こんな感じの流れでできると思います。

という訳で実装。

    class AutoTest
    {
        private const string QuestionFolder = "q";
        private const string AnswerFolder = "a";
        private const string ResultFolder = "r";
        private const string QuestionFile = "save";
        private const string AnswerFile = "output";
        private const string ResultFile = "result";

        public AutoTest()
        {

        }

        public void Run()
        {
            var files = Directory.EnumerateFiles(QuestionFolder, "*");
            foreach(var file in files)
            {
                string answerFile = file.Replace(QuestionFolder, AnswerFolder).Replace(QuestionFile, AnswerFile);
                string resultFile = file.Replace(QuestionFolder, ResultFolder).Replace(QuestionFile, ResultFile);
                if (File.Exists(resultFile))
                {
                    File.Delete(resultFile);
                }

                if (File.Exists(answerFile))
                {
                    List<List<NumberSquare>> rowNumberSquare = new List<List<NumberSquare>>();
                    List<List<NumberSquare>> colNumberSquare = new List<List<NumberSquare>>();
                    FileAccess.Load(file, ref rowNumberSquare, ref colNumberSquare);
                    PicrossAnalyze picross = new PicrossAnalyze(rowNumberSquare, colNumberSquare);
                    var ret = picross.Run();
                    string answer = string.Empty;
                    for(int r = 0; r < rowNumberSquare.Count; r++)
                    {
                        for(int c = 0; c < colNumberSquare.Count; c++)
                        {
                            if (ret[r, c].IsPainted())
                            {
                                answer += "1";
                            }
                            else
                            {
                                answer += "0";
                            }
                        }
                        answer += "\r\n";
                    }
                    string compare = FileAccess.AnswerLoad(answerFile);
                    string result;
                    if(compare.CompareTo(answer) == 0)
                    {
                        result = "OK";
                    }
                    else
                    {
                        result = answer;
                    }
                    using (var stream = new StreamWriter(resultFile, true))
                    {
                        stream.Write(result);
                    }
                }
            }
        }
    }
    class PicrossUI
    {
        public void Run()
        {

            // テストボタン
            var testButton = new Button(10, 170, "テスト");
            testButton.Show();
            asd.Engine.AddObject2D(testButton.getBackTexture());
            asd.Engine.AddObject2D(testButton.getTextObject());
            buttons.Add(testButton);
            testButton.SetAction(() =>
            {
                var test = new AutoTest();
                test.Run();
            });

Directory.EnumerateFiles()で問題ファイルの一覧を取得し、それをforeachで回します。

問題ファイルからデータを取り出し、ピクロス解析処理にかけます。

その結果を前回のような0と1の文字列に変換し、CompareTo()で回答結果との照合を行います。

結果、問題無ければOK、問題があれば解析結果をresultファイルに出力します。

これでテストも楽になる!

【ラズパイ】【GLCD】カレンダーを表示する

GLCDにカレンダーを表示させたいと思います。

import RPi.GPIO as GPIO
import time
import datetime
import calendar
import GLCD
import AM2320
import Weather
import SW


def __main__():
    GPIO.setmode(GPIO.BCM)
    GLCD.PinsInit(20, 7, 8, 9, 18, 19, 10, 11, 12, 13, 14, 15, 16, 17)
    GLCD.GLCDInit()
    GLCD.GLCDDisplayClear()

    try:
        while True:
            cal = calendar.month(datetime.datetime.now().year, datetime.datetime.now().month)
            cals = cal.split("\n")
            y = 0
            for c in cals:
                GLCD.GLCDPuts(1, y, c)
                y += 8

            time.sleep(0.1)
    except KeyboardInterrupt:
        GLCD.GLCDDisplayClear()
        GPIO.cleanup()

__main__()

calendar.month(year, month)で現在月のカレンダーを文字列で出力されます。

これは改行文字を使って整形されているので、改行文字で分割し一行ずつ表示位置を変えながら表示させています。

出来れば今日の日付の部分が分かるようにしたかったんだけど、無理っぽい。

よし、これでやりたいことは一通り出来た。

【ラズパイ】スイッチをモジュール化する

前回までの状況はこちら。

前回作成したサンプルソースコードを元に、スイッチ処理をモジュール化します。

やり方としては、スイッチの押下検出をスレッドで動かしてメインスレッドで取り出す、という方式が良いと思います。

GPIOのエッジ検出で割り込み処理する、という方法もありますが、このスイッチモジュールの仕様上、それは難しそうです。

import RPi.GPIO as GPIO
import time
import threading

X_p = 0
Y_p = 0
Z_p = 0
A_p = 0
B_p = 0
C_p = 0
D_p = 0

Non = ""
Key = Non

thread = None
Loop = True

def PinsInit(x, y, z, a, b, c, d):
 省略

def Start():
    thread = threading.Thread(target=threadProcess)
    thread.start()

def Stop():
    global Loop
    Loop = False

def GetKey():
    global Key
    ret = Key
    Key = Non
    return ret

def threadProcess():
    global Key

    col = 1
    swState = [0] * 12
    while Loop:
        if col == 1:
            GPIO.output(X_p, GPIO.LOW)
            GPIO.output(Y_p, GPIO.HIGH)
            GPIO.output(Z_p, GPIO.HIGH)
        elif col == 2:
            GPIO.output(X_p, GPIO.HIGH)
            GPIO.output(Y_p, GPIO.LOW)
            GPIO.output(Z_p, GPIO.HIGH)
        else:
            GPIO.output(X_p, GPIO.HIGH)
            GPIO.output(Y_p, GPIO.HIGH)
            GPIO.output(Z_p, GPIO.LOW)

        # 1押下
        if swState[1] == 0 and col == 1 and GPIO.input(D_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(D_p) == 0:
                Key = "1"
                swState[1] = 1
        # 1押下戻し
        elif swState[1] == 1 and col == 1 and GPIO.input(D_p) == 1:
            Key = Non
            swState[1] = 0
        # 2押下
        if swState[2] == 0 and col == 2 and GPIO.input(D_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(D_p) == 0:
                Key = "2"
                swState[2] = 1
        # 2押下戻し
        elif swState[2] == 1 and col == 2 and GPIO.input(D_p) == 1:
            Key = Non
            swState[2] = 0
        # 3押下
        if swState[3] == 0 and col == 3 and GPIO.input(D_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(D_p) == 0:
                Key = "3"
                swState[3] = 1
        # 3押下戻し
        elif swState[3] == 1 and col == 3 and GPIO.input(D_p) == 1:
            Key = Non
            swState[3] = 0
        # 4押下
        if swState[4] == 0 and col == 1 and GPIO.input(C_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(C_p) == 0:
                Key = "4"
                swState[4] = 1
        # 4押下戻し
        elif swState[4] == 1 and col == 1 and GPIO.input(C_p) == 1:
            Key = Non
            swState[4] = 0
        # 5押下
        if swState[5] == 0 and col == 2 and GPIO.input(C_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(C_p) == 0:
                Key = "5"
                swState[5] = 1
        # 5押下戻し
        elif swState[5] == 1 and col == 2 and GPIO.input(C_p) == 1:
            Key = Non
            swState[5] = 0
        # 6押下
        if swState[6] == 0 and col == 3 and GPIO.input(C_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(C_p) == 0:
                Key = "6"
                swState[6] = 1
        # 6押下戻し
        elif swState[6] == 1 and col == 3 and GPIO.input(C_p) == 1:
            Key = Non
            swState[6] = 0
        # 7押下
        if swState[7] == 0 and col == 1 and GPIO.input(B_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(B_p) == 0:
                Key = "7"
                swState[7] = 1
        # 7押下戻し
        elif swState[7] == 1 and col == 1 and GPIO.input(B_p) == 1:
            Key = Non
            swState[7] = 0
        # 8押下
        if swState[8] == 0 and col == 2 and GPIO.input(B_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(B_p) == 0:
                Key = "8"
                swState[8] = 1
        # 8押下戻し
        elif swState[8] == 1 and col == 2 and GPIO.input(B_p) == 1:
            Key = Non
            swState[8] = 0
        # 9押下
        if swState[9] == 0 and col == 3 and GPIO.input(B_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(B_p) == 0:
                Key = "9"
                swState[9] = 1
        # 9押下戻し
        elif swState[9] == 1 and col == 3 and GPIO.input(B_p) == 1:
            Key = Non
            swState[9] = 0
        # *押下
        if swState[10] == 0 and col == 1 and GPIO.input(A_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(A_p) == 0:
                Key = "*"
                swState[10] = 1
        # *押下戻し
        elif swState[10] == 1 and col == 1 and GPIO.input(A_p) == 1:
            Key = Non
            swState[10] = 0
        # 0押下
        if swState[0] == 0 and col == 2 and GPIO.input(A_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(A_p) == 0:
                Key = "0"
                swState[0] = 1
        # 0押下戻し
        elif swState[0] == 1 and col == 2 and GPIO.input(A_p) == 1:
            Key = Non
            swState[0] = 0
        # #押下
        if swState[11] == 0 and col == 3 and GPIO.input(A_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(A_p) == 0:
                Key = "#"
                swState[11] = 1
        # #押下戻し
        elif swState[11] == 1 and col == 3 and GPIO.input(A_p) == 1:
            Key = Non
            swState[11] = 0

        col += 1
        if col > 3:
            col = 1
        
        time.sleep(0.005)

Start()でthreadProcess()をバックグラウンドで実行、Stop()でLoop=Falseになるまで実行します。

スイッチを押すと、その値がKeyに入るので、それをGetKey()で取り出します。

変数でワンクッション置いているのは、ボタン押しっぱなしで複数回キー入力を検出するのを防ぐためです。

def __main__():
    GPIO.setmode(GPIO.BCM)

    SW.PinsInit(21, 22, 23, 24, 25, 26, 27)
    SW.Start()

    try:
        while True:
            key = SW.GetKey()
            if key != "":
                print(key)

            time.sleep(0.1)
    except KeyboardInterrupt:
        SW.Stop()
        GPIO.cleanup()

__main__()

ここまで出来れば、他モジュールと組み合わせることも出来そうです。

【北海道大戦】対戦ルールを検討する

さて、前回までで、マップ関連は完成しました。

次回からバトル関連を作成していこうと思うのですが、バトルのルールを決めておく必要があります。

まずは、コンピューターだけでバトルさせて、どの市町村が勝利するかを見てみたいと思います。

ということで以下の様にしていました。

  • 各市町村、1ターンに1回行動できる
  • 行動順はランダムで並び替えされる
  • 1ターンにつき、隣接する市町村に攻撃を仕掛けることが出来る
  • 戦力値は人口×0.5~3.0(乱数)で決定される
  • 戦力値が大きい方が勝利する
  • 攻撃側が勝利した場合は防御側の市町村を吸収合併する。
  • 防御側が勝利しても何も無し。
  • 最終的に市町村が残り1つになるまで繰り返す。

こんな感じでどうだろうか?

まずは、作ってみて動かしてみましょうか。

【北海道大戦】離島問題を解決する

前回までの状況はこちら。

最新ソースはこちら(gitHub)

https://github.com/takishita2nd/HokkaidoWar

さて、前回で隣接都市とのリンクを作成しましたが、

1つ問題がありまして、

それは離島とのリンクです。

今のままではマスの隣接関係でリンクを確認しているので、マスが離れている離島との隣接関係がありません。

具体的には、

  • 奥尻ー江差
  • 礼文ー稚内
  • 礼文ー利尻
  • 礼文ー利尻富士
  • 利尻富士ー稚内

この隣接関係を作成しなければ行けません。

※ちなみに、天売島、焼尻島は羽幌に属しています。

今回はUp(),Down(),Left(),Right()処理に例外を入れることで対応します。

マップそのものが変わってしまうと、ここの処理も修正しなければ鳴りませんが、他に有効な手段が思いつかないので、仕方がありません。

    class Map
    {
        public Map Up { 
            get {
                var field = Singleton.GetFieldMap();
                if(_x == 18 && _y == 0)
                {
                    return field.GetMap(23, 0);
                }
                else if(_x == 23 && _y == 0)
                {
                    return field.GetMap(18, 0);
                }
                else if (_x == 20 && _y == 1)
                {
                    return field.GetMap(18, 0);
                }
                else
                {
                    return field.GetMap(_x, _y - 1);
                }
            }
        }

        public Map Down
        {
            get
            {
                var field = Singleton.GetFieldMap();
                if(_x == 18 && _y == 0)
                {
                    return field.GetMap(19, 1);
                }
                else
                {
                    return field.GetMap(_x, _y + 1);
                }
            }
        }

        public Map Left
        {
            get
            {
                var field = Singleton.GetFieldMap();
                if (_x == 2 && _y == 29)
                {
                    return field.GetMap(0, 29);
                }
                else if(_x == 19 && _y == 1)
                {
                    return field.GetMap(18, 0);
                }
                else if (_x == 23 && _y == 0)
                {
                    return field.GetMap(20, 1);
                }
                else
                {
                    return field.GetMap(_x - 1, _y);
                }
            }
        }

        public Map Right
        {
            get
            {
                var field = Singleton.GetFieldMap();
                if (_x == 0 && _y == 29)
                {
                    return field.GetMap(2, 29);
                }
                else if(_x == 18 && _y == 0)
                {
                    return field.GetMap(20, 1);
                }
                else if (_x == 20 && _y == 1)
                {
                    return field.GetMap(23, 0);
                }
                else
                {
                    return field.GetMap(_x + 1, _y);
                }
            }
        }

こんな感じで離島とのリンク関係を強引に作りました。

【ダイエット支援】食事管理機能を検討する。

ダイエット支援ツールは以下のリンク先で運用中です。

https://taki-lab.site:8443

ダイエット支援ツールに食事管理機能を追加したいと思います。

ダッシュボードはこんな感じにしようと思います。

三栄要素をレーダーチャートで表示させます。

Chart.jsを調べたらこんな感じのチャートが表示出来るみたいです。

また、この画面からデータ入力も出来るようにしたいと思います。

詳細画面はこんな感じ。

日付毎に三栄要素とカロリーを一覧表示。

さらに編集画面へのリンクを用意します。

編集画面はこんな感じ。

朝昼晩毎に、食事と栄養素とカロリーを表示します。

入力、編集も可能にします。

その他、ちょっとFitBitアプリの機能っぽいものに仕上げたいと思います。

じゃあ、次回から取りかかりますか。

【ラズパイ】スイッチを動かしてみる。

前回までの状況はこちら。

前回はスイッチをはんだ付けして、ブレッドボードに取り付けました。

今回はこのスイッチを動かしてみます。

import RPi.GPIO as GPIO
import time

X_p = 0
Y_p = 0
Z_p = 0
A_p = 0
B_p = 0
C_p = 0
D_p = 0

def PinsInit(x, y, z, a, b, c, d):
    global X_p
    global Y_p
    global Z_p
    global A_p
    global B_p
    global C_p
    global D_p

    X_p = x
    Y_p = y
    Z_p = z
    A_p = a
    B_p = b
    C_p = c
    D_p = d

    GPIO.setup(X_p, GPIO.OUT)
    GPIO.setup(Y_p, GPIO.OUT)
    GPIO.setup(Z_p, GPIO.OUT)
    GPIO.setup(A_p, GPIO.IN)
    GPIO.setup(B_p, GPIO.IN)
    GPIO.setup(C_p, GPIO.IN)
    GPIO.setup(D_p, GPIO.IN)

    GPIO.output(X_p, GPIO.HIGH)
    GPIO.output(Y_p, GPIO.HIGH)
    GPIO.output(Z_p, GPIO.HIGH)

def SW_Sample():
    col = 1
    swState = [0] * 12
    while True:
        if col == 1:
            GPIO.output(X_p, GPIO.LOW)
            GPIO.output(Y_p, GPIO.HIGH)
            GPIO.output(Z_p, GPIO.HIGH)
        elif col == 2:
            GPIO.output(X_p, GPIO.HIGH)
            GPIO.output(Y_p, GPIO.LOW)
            GPIO.output(Z_p, GPIO.HIGH)
        else:
            GPIO.output(X_p, GPIO.HIGH)
            GPIO.output(Y_p, GPIO.HIGH)
            GPIO.output(Z_p, GPIO.LOW)

        # 1押下
        if swState[1] == 0 and col == 1 and GPIO.input(D_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(D_p) == 0:
                print("push 1")
                swState[1] = 1
        # 1押下戻し
        elif swState[1] == 1 and col == 1 and GPIO.input(D_p) == 1:
            print("release 1")
            swState[1] = 0
        # 2押下
        if swState[2] == 0 and col == 2 and GPIO.input(D_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(D_p) == 0:
                print("push 2")
                swState[2] = 1
        # 2押下戻し
        elif swState[2] == 1 and col == 2 and GPIO.input(D_p) == 1:
            print("release 2")
            swState[2] = 0
        # 3押下
        if swState[3] == 0 and col == 3 and GPIO.input(D_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(D_p) == 0:
                print("push 3")
                swState[3] = 1
        # 3押下戻し
        elif swState[3] == 1 and col == 3 and GPIO.input(D_p) == 1:
            print("release 3")
            swState[3] = 0
        # 4押下
        if swState[4] == 0 and col == 1 and GPIO.input(C_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(C_p) == 0:
                print("push 4")
                swState[4] = 1
        # 4押下戻し
        elif swState[4] == 1 and col == 1 and GPIO.input(C_p) == 1:
            print("release 4")
            swState[4] = 0
        # 5押下
        if swState[5] == 0 and col == 2 and GPIO.input(C_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(C_p) == 0:
                print("push 5")
                swState[5] = 1
        # 5押下戻し
        elif swState[5] == 1 and col == 2 and GPIO.input(C_p) == 1:
            print("release 5")
            swState[5] = 0
        # 6押下
        if swState[6] == 0 and col == 3 and GPIO.input(C_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(C_p) == 0:
                print("push 6")
                swState[6] = 1
        # 6押下戻し
        elif swState[6] == 1 and col == 3 and GPIO.input(C_p) == 1:
            print("release 6")
            swState[6] = 0
        # 7押下
        if swState[7] == 0 and col == 1 and GPIO.input(B_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(B_p) == 0:
                print("push 7")
                swState[7] = 1
        # 7押下戻し
        elif swState[7] == 1 and col == 1 and GPIO.input(B_p) == 1:
            print("release 7")
            swState[7] = 0
        # 8押下
        if swState[8] == 0 and col == 2 and GPIO.input(B_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(B_p) == 0:
                print("push 8")
                swState[8] = 1
        # 8押下戻し
        elif swState[8] == 1 and col == 2 and GPIO.input(B_p) == 1:
            print("release 8")
            swState[8] = 0
        # 9押下
        if swState[9] == 0 and col == 3 and GPIO.input(B_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(B_p) == 0:
                print("push 9")
                swState[9] = 1
        # 9押下戻し
        elif swState[9] == 1 and col == 3 and GPIO.input(B_p) == 1:
            print("release 9")
            swState[9] = 0
        # *押下
        if swState[10] == 0 and col == 1 and GPIO.input(A_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(A_p) == 0:
                print("push *")
                swState[10] = 1
        # *押下戻し
        elif swState[10] == 1 and col == 1 and GPIO.input(A_p) == 1:
            print("release *")
            swState[10] = 0
        # 0押下
        if swState[0] == 0 and col == 2 and GPIO.input(A_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(A_p) == 0:
                print("push 0")
                swState[0] = 1
        # 0押下戻し
        elif swState[0] == 1 and col == 2 and GPIO.input(A_p) == 1:
            print("release 0")
            swState[0] = 0
        # #押下
        if swState[11] == 0 and col == 3 and GPIO.input(A_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(A_p) == 0:
                print("push #")
                swState[11] = 1
        # #押下戻し
        elif swState[11] == 1 and col == 3 and GPIO.input(A_p) == 1:
            print("release #")
            swState[11] = 0

        col += 1
        if col > 3:
            col = 1
def __main__():
    GPIO.setmode(GPIO.BCM)
    GLCD.PinsInit(20, 7, 8, 9, 18, 19, 10, 11, 12, 13, 14, 15, 16, 17)
    GLCD.GLCDInit()
    GLCD.GLCDDisplayClear()

    roop = 10 * 60 * 60
    try:
        while True:
            SW.PinsInit(21, 22, 23, 24, 25, 26, 27)
            SW.SW_Sample()
            time.sleep(0.1)
    except KeyboardInterrupt:
        GLCD.GLCDDisplayClear()
        GPIO.cleanup()

__main__()

思った以上に長くなった。

まずピン設定ですが、X,Y,Zに繋がるピンが出力用、A,B,C,Dに繋がるピンが入力用になります。

そして、X,Y,ZはHIGHで初期化します。

サンプル処理では、whileの無限ループの中で、

X=LOW、Y=HIGH、Z=HIGH

X=HIGH、Y=LOW、Z=HIGH

X=HIGH、Y=HIGH、Z=LOW

を繰り返します。

その中で、A~Dの中にLOWの信号があれば、X,Y,ZのLOW出力の状態とA~DのLOW入力の状態を

この表に当てはめて、押下されたボタンを特定します。

ただ、無限ループで動いているので、普通に信号だけを見ていると、ものすごい数の入力をプログラムが検出してしまうので、スイッチの状態を保持し、その値と変化があるか、ということを確認する必要があります。

また、物理スイッチが入ったとき、信号が一瞬の短い間、HIGH/LOWを繰り返すような信号になります。

これをチャタリングといいます。

何も対策していないと、スイッチ1回しか押していなくても、プログラムは数回スイッチを押したと判断してしまうことがあります。

なので、それを回避するために、スイッチの検出を行った後、50ミリ秒後にもう一度信号を確認、信号に変化が無ければスイッチオンとする、という風にする必要があります。

実行結果はこうなりました。

pi@raspberrypi:~/RaspiDisplayMonitor $ python3 main.py
push 1
release 1
push 2
release 2
push 3
release 3
push 4
release 4
push 5
release 5
push 6
release 6
push 7
release 7
push 8
release 8
push 9
release 9
push *
release *
push 0
release 0
push #
release #
push 1
push #
release 1
release #
push 2
push 8
release 8
push 9
release 9
release 2

同時押しも動きましたね。

これで基本的な動きは出来ました。

次回は、これをモジュール化して今までのプログラムと組み合わせていましょう。

【C#】【ピクロス】【ALTSEED】解析結果をファイルに出力する

前回までの状況はこちら。

最新ソースはこちら。(gitHub)

https://github.com/takishita2nd/Picross

今回からは、デバッグ作業をよりやりやすくするために、過去問を一括して解いて正しく解析できているかを確認する仕組みを作っていきます。

そのためには、解析結果をファイルに出力して、照合対象となるデータを作成する必要があります。

なので、以下の様なコードを作成しました。

    class FileAccess
    {
        private const string _outputfile = "output.dat";
        public static void Output(BitmapData[,] bitmapDatas, int row, int col)
        {
            if (File.Exists(_outputfile) == true)
            {
                File.Delete(_outputfile);
            }

            using (var stream = new StreamWriter(_outputfile, true))
            {
                for(int r = 0; r < row; r++)
                {
                    for(int c = 0; c < col; c++)
                    {
                        if(bitmapDatas[r, c].IsPainted())
                        {
                            stream.Write(1);
                        }
                        else
                        {
                            stream.Write(0);
                        }
                    }
                    stream.Write("\r\n");
                }
            }
        }

解析済みのデータbitmapDatasを受け取り、それをファイルに出力します。

色塗りの部分は1、マスク部分は0となるイメージです。

このデータが、

こんな感じになります。

問題データを

こんな感じで用意して、

解析結果データを、

こんな感じで作成しました。

数字で問題と解答が結びついているイメージです。

さて、これで必要な準備が出来ました。

毎回、ピクロスネタをやるとコーディングに膨大な時間を使ってしまうので、今回はサックリやって、続きは次回やります。