■競技プログラミング AtCoder Beginner Contest 133 A問題

めげてないで努力してみましょう。

コンテストをよく見てみると「AtCoder Beginner Contest」というのがありました。先日飛びついたのは「AtCoder Grand Contest」でしたので、そりゃ難しいに決まってます (^_^;)

 

AtCoder Beginner Contest 133 の A 問題「T or T」です。

 

問題文
私たちは N 人で旅行しようとしており、その交通手段として電車とタクシーがあります。

電車を使うと 1 人あたり A 円かかります。

タクシーを使うと N 人で B 円かかります。

全員の交通費の合計は最小でいくらになるでしょうか。

 

なんか算数の問題っぽくってよさげなんですが、またしても問題の意味がわからない。「全員の交通費の合計は最小でいくらになるでしょうか」ってどーゆーこと?

 

例 1 を見てみましょう。人数 N が 4 人、電車 A が 2 円でタクシー B が 9 円とすると、電車が 4 人× 2 円で 8 円、タクシーが 4 人で 9 円ですから、全員の交通費の合計の最小値は 8 円、だそうです。や、安すぎる (^_^;)

えーとつまり、「交通費が安くなる交通手段はどっちか」ってことか。20 人いたらタクシーだと 5 台必要だから… なんてこと考えなくていいのね?

うぅ、なんかあまりに現実離れしていて頭がついていかない… orz

 

例 2 では、N=4 A=2 B=7 ですから、電車が 8 円、タクシーが 7 円で、出力は 7 。

例 3 では、N=4 A=2 B=8 で、電車もタクシーも同額の 8円で、出力は 8 となります。

 

わかりましたよ。こうしましょう。

 

  1. 数値の入力
    scanf() で変数にそれぞれ人数、電車の運賃、タクシーの運賃を入力します。
  2. 範囲の判定
    入力値が範囲外かどうかを判定して、範囲外の時は Error! を表示してプログラムを終了 (return 1;) します。たぶん、この制約の範囲内でプログラムを考えればいいよってことで、エラー処理はしなくてもいいみたいですけど。
  3. 最小値の計算と抽出
    最初は安いほうの運賃で分岐してどちらかを表示させる方法を考えたのですが、処理部分と表示部分は分けたほうがわかりいいよねってことで分けました。
    まず電車賃を最小値 lowestFare としておき、次にタクシー代と比較して、もしタクシー代の方が安ければ lowestFare を更新するという単純な方法です。
  4. 最小値の表示
    printf() で最小値 lowestFare を表示します。

 

で、こんなふうになりました。

 

/*  AtCoder Beginner Contest 133 A - T or T
    2019.07.24 by meyon  */

 

#include <stdio.h>

 

int main()
{
  // data input and judgment
  int N;    // number of people
  int A;    // train fare
  int B;    // taxi fare
  scanf("%d %d %d", &N, &A, &B);
  if(N<1 || N>20 || A<1 || A>50 || B<1 || B>50) {
    printf("Error!¥n");
    return 1;
  }

 

  // extract the lowest fare
  int lowestFare = N * A;
  if(lowestFare > B) {
    lowestFare = B;
  }

 

  // display the lowest fare
  printf("%d¥n", lowestFare);

 

  return 0;
}

 

なんだかね、やれそうな気がしてきましたよ (^_^;)

 

2019.07.26 追記

過去問題でも提出できることを知りました。

で、提出してみましたら、結果「AC」でしたよ。めでたい \(^o^)/

 


■競技プログラミング過去問題をやろうとしてみた…

やってみたんじゃなくて、やろうとしてみて、門前でめげたとゆ〜話 orz

 

C 言語を勉強しようと思い、なにか例題はないかなぁと探していたら、いいのがありました。

AtCoder:競技プログラミングコンテストを開催する国内最大のサイト

さっそく過去の問題に挑戦してみましょう。

 

7月14日の AtCoder Grand Contest 035 の A 問題「XOR Circle」です。

 

問題文
すぬけ君は N 枚の帽子を持っています。i 枚目の帽子には整数 ai が書かれています。

N 頭のラクダが円環状に並んでいます。 すぬけ君はそれぞれのラクダに 1 枚の帽子を被せようとしています。

どのラクダについても以下の条件が成立するような帽子の被せ方が存在するならば ' Yes ' を、そうでなければ ' No ' を出力してください。

・両隣のラクダが被っている帽子に書かれた数のビットごとの排他的論理和が自身の被っている帽子に書かれた数と等しい

 

すぬけ君って (^_^;) 「競技プログラミング_用語 - projecthikky @ ウィキ - アットウィキ」によると「AtCoder社の社員のレッドコーダーsnukeさんのこと」だそうです。

 

しかし、えっと、あの〜、なんてゆ〜か、問題の意味すらわかりません orz

 

だいたい、すぬけ君はなぜラクダに帽子を被せようとしているわけ? その帽子に書かれた数の排他的論理和が云々ってなんの意味があるん (?_?) 

あ〜そうか、なんか小難しい数値の処理をラクダと帽子に例えたってことね。

登場人物の名前といい、たとえ話の奇想天外さといい、俺みたいな一般人が容易に足を踏み込めそうにないオタクなにおいがしてくる… (^_^;)

 

閑話休題

 

最も理解できなかったのは「自身の被っている帽子」ってところ。帽子はラクダに被せるんでしょ? すぬけ君の被っている帽子ってなによ (?_?) ―― 考えること小一時間、気が付いた。「自身」というのは両隣のラクダに挟まれたラクダのことね。「自身」というからてっきり人だと思った。

つまりこういうこと。

あるラクダの両隣のラクダが被っている帽子に書かれた数のビットごとの排他的論理和が、そのラクダ自身の被っている帽子に書かれた数と等しい」

 

例 1 より、帽子の数 N を 3 、それぞれの帽子に書かれている数字 a を 1 、2 、3 とします。

あるラクダ自身の帽子に書かれた数が 1 だとすると、右隣のラクダの帽子に書かれた数が 3 で、左隣のラクダの帽子に書かれた数は 2 です。右隣の 3 を二進数表示すると 0b11 、左隣の 2 は 0b10 です。それらのビットごとの排他的論理和は 0b01 になります。ラクダ自身は 0b01 で、求めた値と等しくなりますから、与えられた条件は「真」です。

帽子に書かれた数が 2 のラクダについても、両隣のラクダの帽子に書かれた数 1 と 3 のビットごとの排他的論理和は 0b10 になりますから「真」、3 のラクダについても同様に「真」です。

ということで、「どのラクダについても条件が成立」したので ' Yes ' を出力します。

 

では例 2 はどうでしょう。N = 4 、a = 1 2 4 8 とします。

ラクダ a=1 (0b0001) の右隣のラクダを a=8 (0b1000) 、左隣のラクダを a=2 (0b0010) とすると XOR は 10 (0b1010) ですから「偽」です。

両隣を 8 と 4 にしてみても、2 と 4 にしてみても「偽」となり、a=1 について条件を満たすような帽子の被せ方は存在しません。

「どのラクダについても条件が成立する」ことが条件ですから、これ以上他のラクダを検討してみる必要はないでしょう。この場合は ' No ' を出力します。

 

ということで、ようやく問題の意味が理解できました。

 

では問題に従って 3 ≦ N ≦ 10、0 ≦ ai ≦ 109 について考えてみましょう …

 

わ、わっかりませ〜ん orz

 

ラクダの頭数が 10 万頭で、帽子に書かれる数は 10 億までの範囲のラクダの頭数分の個数。帽子に書かれた数がすべて異なるといった条件はありませんから、同じ数が書かれた帽子もありうると… あ〜、力業で総当たりするにしても、どうすればよいものやらさっぱり (^_^;)

つまりこれは、C だのなんだの言う前に、いわゆる「アルゴリズム」ってものをしっかり考えろというとても基本的な命題なのだと、改めて気が付いた meyon さんでした。

 


■電源ユニットに端子板を付ける

余っていたパソコンの電源ユニットを処分しようとしたんだけど、ふと、これ電源装置として使えるよなぁと思ったわけ。で調べてみました。

 

出力は +12V が二系統、+5V、+3.3V が各一系統と -12V 一系統。その他にスタンバイ用の +5VSB があります。

+5VSB は電源 ON で常に 5V が出力されていますが、他は出力していません。PS_ON という信号線を LOW に落とすと冷却ファンが回りだし、すべての出力が ON 。出力が ON になると PWR_OK が HIGH になります。

その他に 3.3Vsence というラインがありますが、これは +3.3V の出力検出用で、マザーボードに挿入するメインコネクタで +3.3V 出力に繋がっていました。

 

参考:ニプロン 製品情報 電源事典 Q&A Q. ATX電源_EPSのピンアサインについて教えて下さい。

 

 

電源ユニットからは、マザーボードや周辺機器を接続するために、さまざまな形のコネクタが付いたハーネスがたくさん出ています。これらを取り外して、出力と制御用に端子板を取り付けることにします。


電源ユニット

 

 

カバーを開けると、基板の出力部周辺はこんな感じ。同じ場所から何本もの電線が束になって出ていることがわかります。


出力線のようす

 

 

基板をひっくり返してみるとこんな感じ。大量のハンダが盛られていて、外し甲斐がありそうです (^_^;)


基板裏面のようす

 

 

電線を取り外しましたが、けっこう大変な作業でした。ハンダを取るとき、周辺の抵抗器などのハンダまで溶かしたり、パターンをショートしたりしないように注意が必要です。

信号線など一本だけ出ているところはそのまま残しています。


電線を取り外した基板

 

 

新たに出力線をハンダ付けし、ケースに戻します。


出力線を取り付け

 

 

ケースに端子板取付用の穴あけをします。ボール盤があればもっときれいにあけられるんですけどねぇ (言い訳 ^_^;)


ケースに端子板用の穴あけ

 

 

端子板を取り付け、出力線をハンダ付けして組み立てたら、完成です。


完成

 

 

端子板は以下のようになっています。3.3Vsence は No.4 端子に接続してあります。

 

No. 1 2 3 4 5 6 7 8 9 10
出力 12V(1) 12V(2) 5V 3.3V GND GND PS_ON 5VSB PWR_OK -12V

 

今回は 10pin の端子板を取り付けたのですが、これだと GND 端子が二つしか取れません。GND 端子は余裕があったほうが使いやすいので、12pin の端子板にしたほうがよかったかなと思っています。あと、端子板の表示をつけないといけませんね。

 


■ArduinoでFizzBuzzしてみた

俺はプログラミングもほとんど初心者に等しいので、そっちの方をもう少し勉強しようと思ってます。

Arduino スケッチは C 言語をベースにしているということなので、C 言語も合わせてやっていきましょう。てなことを考えながら、とりあえず何か書いてみるなら FizzBuzz かなとか (^_^;)

 

FizzBuzz とは、まぁ誰でも知っていると思いますけど、50 まで数えて 3 の倍数のときアホになるというやつです。… じゃぁなくて、100 までカウントして 3 の倍数のとき Fizz 、5 の倍数のとき Buzz、さらに 3 と 5 の倍数のときは FizzBuzz と表示させるといったプログラムのことです。こいつを Arduino でやってみます。

ちょうど 7セグメント LED を使ってみていたので、スタートボタンを押したら 0 から 99 までカウント表示させることにしましょう。3 の倍数のときは FZ 、5 の倍数では BZ 、3 と 5 の倍数のときは FB と表示させることにします。

 

回路図は以下です。特段変わったところはありません。

Arduino の D9 にタクトスイッチをつなぎ、これを ON するとカウントをスタートさせます。

アノードコモンの 7 セグメント LED 表示器が 2個手元にありますので、これをシフトレジスタ SN74LS164 で表示させます。デジットドライブはこれも手持ちの 2N3906 を使用しています。2SA1015 とかでもいいですね。FET でもかまいません。持っているものを適当に使いましょう。でも Arduino で直接駆動するのはちょっと無理があります。

シフトレジスタと LED の接続は、7ビットのデータを LSBFIRST (最下位ビットから送出) させるつもりなので、Qa の出力を未接続にしています。このあたりはプログラムと整合させないとうまく表示できませんので注意します。

 


DizzBuzz_回路図

 

 

スケッチのフローチャートです。

スイッチの ON を検出して 0 から 99 までカウントします。それを 7 セグメント LED で 2 桁表示させます。十の位が 0 のときは表示しないようにしています。2 桁を 1 ミリ秒ずつ表示して 300 回繰り返すので、約 600 ミリ秒ごとにカウントしていくことになります。

その値の倍数かどうかは剰余が 0 かどうかを判断します。3 と 5 の倍数ということは 15 の倍数ということです。ここが FizzBuzz の肝ですね。それぞれの判定結果に従って、表示する値を変えています。

ん〜、なんかもっとすっきりできそうな気がするけど、いまの俺のレベルではこんなもんでしょ (^_^;)

 


FizzBuzz_フローチャート

 

 

スケッチです。

じつは、関数の引数とか戻り値とか初めて使ってみたんですよ (^_^;)

 

// Fizz Buzz on Arduino  2019.06.16 meyon

 

/*  スイッチを押下すると 0 から 99 までカウントする。
    3 の倍数のとき FZ 、5 の倍数のとき BZ 、
    15 の倍数のとき FB と表示させる。
*/

 

const byte segmentData[14] = {
  B1111110, // 0
  B0110000, // 1
  B1101101, // 2
  B1111001, // 3
  B0110011, // 4
  B1011011, // 5
  B1011111, // 6
  B1110000, // 7
  B1111111, // 8
  B1111011, // 9
  B1000111, // F
  B0011111, // B
  B1101100, // Z
  B0000000  // blank
};

 

const int startPin = 9;
const int driverPin[2] = {10, 11};
const int clockPin = 12;
const int dataPin = 13;

 


int judgeNumber(int value) {
  int result;
  if(value == 0) {
    result = 0;
  } else if(value % 15 == 0) {
    result = 15;
  } else if(value % 5 == 0) {
    result = 5;
  } else if(value % 3 == 0) {
    result = 3;
  } else {
    result = value;
  }
  return result;
}

 


void displayNumber(int value) {
  int digit[2];
  if(value == 15) {
    digit[0] = 11;        // B
    digit[1] = 10;        // F
  } else if(value == 5) {
    digit[0] = 12;        // Z
    digit[1] = 11;        // B
  } else if(value == 3) {
    digit[0] = 12;        // Z
    digit[1] = 10;        // F
  } else {
    digit[0] = value % 10;
    if(value / 10 == 0) {
      digit[1] = 13;      // 10 digit is blank
    } else {
      digit[1] = value / 10;
    }
  }

 

  int j=0;
  while(++j < 300) {      // Display cycle
    for(int i=0; i<2; i++) {
      shiftOut(dataPin, clockPin, LSBFIRST, ~segmentData[digit[i]]);
      digitalWrite(driverPin[i], LOW);
      delay(1);
      digitalWrite(driverPin[i], HIGH);
    }
  }
  return;
}

 

 

void setup() {
  pinMode(startPin, INPUT);
  pinMode(driverPin[0], OUTPUT);
  pinMode(driverPin[1], OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);

  digitalWrite(driverPin[0], HIGH);
  digitalWrite(driverPin[1], HIGH);
}

 


void loop() {
  if(digitalRead(startPin) == LOW) {
    for(int i=0; i<100; i++) {
      int value = judgeNumber(i);
      displayNumber(value);
    }
  }
}

 

 

ついでにブレッドボードです。

7 セグメント LED がピンが左右についている品物なので 2桁並べて置けません。ご愛嬌です。

回路図にはありませんが、IC の電源には 0.1μF のコンデンサをつけてます。またブレッドボードの電源ラインには 10μF 程度の電解コンデンサを置くようにしています。
 


FizzBuzz_ブレッドボード

 

 

電源が写真にはありませんが、12V の AC アダプタの出力から 3 端子レギュレータで 5V を作っています。Arduino の電源は 12V を Vin に供給しています。ただ、USB ケーブルを挿したまま AC アダプタを切っちゃうと Vin から 5V が逆流してしまうので、12V ラインにダイオードを入れて阻止しています。まぁ小ネタですけど。

 


■Arduino Nano 互換機を買ってみた

Amazon で「HiLetgo® 3個セット Mini USB Nano V3.0 ATmega328P CH340G 5V 16M マイクロコントローラーボード Arduinoと互換」を購入しました。結果、何の問題もなく期待通り動作してくれました。

 

廉価な互換機ということなので、Uno との違いもさることながら、正規品との違いも確認しておきたいとググってみたのですが、どうもなにやら良い印象がない (^_^;) 

レビューでも数個に 1個は動作しないとか、USB シリアル変換チップ CH340 用のドライバをインストールしないといけないとか、その CH340 が偽物であるとか、Old Bootloader を選択しないと書き込みできないとか… 使ってみたというサイトを見ても同様の情報が多く、また中国からの配送なので時間がかかるとか、まぁ導入時は多少苦労するかなと覚悟をしていました。

 

が、そんな懸念は一切無用。Ubuntu 18.04.2 LTS のパソコンで、次の設定で動作しています。

 

ボード:Arduino Nano

プロセッサ:ATmega328P

シリアルポート:/dev/ttyUSB0

 

CH340 のドライバのインストールは不要です。ブートローダも Old ではないほうで良いので、新しいものになっているようです。ちなみに CH340 の真贋の判断基準となるとか言われているマークは底の平らな大きな丸でした。見た目も気になるようなところは何もなく、粗悪な安物といった感じはありません。


Arduino nano 互換機

 

USB ケーブル (A to miniB) とその他  1点とともに、注文から約 24時間で到着しました。さすが Amazon's Choice です。寸法はわかっていましたが、実物は思っていた以上に小さく感じました。A コネクタと変わらないですね。

 

まず、3個すべてに Blink を書き込んで正常に動作することを確認しました。

付属のピンヘッダを取り付けて、以前作った 3x3x3 LED Cube につないでみました。これも全く問題なく動作しています。このサイズの基板で実装できそうですね。


3x3x3 LED Cube with 互換機

 

 


■ファンクションボタンの検出

最近よく見かける、上下左右にメニューボタン、中央に決定ボタンがあるような、なんていうのか名前を知らないのですが、ファンクションボタンを作ってみました。

 

タクトスイッチ 6個を「up」「down」「right」「left」「set」「reset」のファンクションボタンとし、どのボタンが押されたかを検出します。今回の肝は電圧分圧方式でのボタン検出で、押したボタンによって入力電圧が変化するという仕組み。ググると考え方や抵抗値の計算方法が出てきますので参考にしてください。

二つ以上のボタンを同時に押した場合は優先順位がありますので、今回は「reset」を最優先のボタンとして並べてみました。

計算上の分電圧と実際の入力値は異なりますので、Arduino の入力値を実測してボタン判断のしきい値を決めています。

どのボタンが押されたかを表示するために、手元にあった 7 セグメント LED (アノードコモン) をシフトレジスタで表示させています。シフトレジスタは前に LED キューブで使っていた SN74LS164 です。こいつはストレージレジスタがありませんので、ドライブ用にトランジスタ 2N3906 を付けました。これによりデータ送信中は LED を表示させないようにしています。セグメントの電流は 7mA 程度、2N3906 は最大コレクタ電流 200mA ですので十分使えます。

 

回路図です。


function-switch 回路図

 

ブレッドボードはこんな感じ。実際はこれを時計方向に 90° 回して実験していますので、右上のタクトスイッチは上下左右に 4 個のボタンと左下に reset 、右下に set という配置になってます。まぁどんな並びでも関係ないんですけど (^_^;)  押したボタンに応じて数字が表示されるという単純なものです。


function-switch ブレッドボード

 

 

フローチャートとスケッチです。ボタン判断のしきい値がフローチャートとスケッチで異なっていますが、最終的にはスケッチの値にしています。

 


画像キャプション

 

// function-switch 2019.06.06 meyon

 

byte segmentData[7] = {
  B1111110, // 0
  B0110000, // 1
  B1101101, // 2
  B1111001, // 3
  B0110011, // 4
  B1011011, // 5
  B1011111  // 6
};

int driverPin = 11;
int clockPin = 12;
int dataPin = 13;
int displyNumber = 0;


int buttonPressed() {
  int inputVoltage = analogRead(A0);

  if (inputVoltage > 947) {
    return 0; //off
  } else if (inputVoltage > 779) {
    return 1; //set
  } else if (inputVoltage > 602) {
    return 3; //up
  } else if (inputVoltage > 436) {
    return 5; //right
  } else if (inputVoltage > 268) {
    return 4; //down
  } else if (inputVoltage > 90) {
    return 6; //left
  } else {
    return 2; //reset
  }
}


void displyButtonNo() {
  digitalWrite (driverPin, HIGH);
  shiftOut(dataPin, clockPin, MSBFIRST, ~segmentData[displyNumber]);
  digitalWrite (driverPin, LOW);
  return;
}


void setup() {
  pinMode (driverPin, OUTPUT);
  pinMode (clockPin, OUTPUT);
  pinMode (dataPin, OUTPUT);

  Serial.begin(9600);
}

 

void loop() {
  Serial.println(analogRead(A0));
  
  if (buttonPressed() != 0) {
    displyNumber = buttonPressed();
  }
  displyButtonNo();
}

 

例によって入力値の確認のためにシリアル出力を使ってます。

シフトレジスタもドライブ用トランジスタも LOW で点灯になります。これ勘違いするとすべてがおかしくなってしまいますので (^_^;)

 


■押しボタンスイッチのアクションの検出

Arduino を使って、押しボタンスイッチの入力アクションの検出を行なってみました。

アクションは「シングル (一回押し) 」「ダブル (二回押し) 」「ホールド (長押し) 」の三種類で、これらを一つのタクトスイッチから検出し LED の点灯・点滅・消灯を制御します。

ググってみると、一定の時間内にスイッチの状態を確認して OFF/ON が変化する時間や回数を調べる方法がとられているようですので、その方法で自分なりに考えてみることにします。

 

 

ますはアクションの検出について。

スイッチに入力があったら、そこから 50ミリ秒毎に 10回チェックを行い、その間にあった変化の回数をカウントすることにします。「シングル」では変化は 1回だけです。「ホールド」では変化はありません。「ダブル」の場合は 2回目の変化があった時点で判断します。


アクションの検出

 

 

次に回路図です。

難しいものではありません。タクトスイッチの入力と、出力で LED を駆動するだけです。

入力ピンの電流を確認してみると HIGH でも LOW でも 0.7mA 程度でしたので、プルアップでもプルダウンでもどちらでもよさそうです。今回は ON 時に HIGH となるようプルダウンとしました。

出力も HIGH 時に LED が点灯するようにしました。もちろん電流値をきちんと確認して Arduino で直接駆動してもかまいませんが、これぐらいの単純なスイッチング回路を作るぐらいは屁でもありませんから (^_^;)


回路図

 


大まかなフローチャートです。

グーグル先生たちはこういうのを書かないのか、あまり見ませんね (^_^;)  上手ではありませんが、考えを整理するためにも簡単に作ると具合がいいです。


フローチャート(全体)


「入力待ち」では、スイッチが押されたかどうかを検知します。待つためにここでプログラムを止めてしまうと LED の点滅の制御ができなくなるので、一度検知したら次の処理を行なってからまた戻ってくるようにします。

スイッチが押されたら「変化を調べる」処理をします。前述のように、一定の時間内にスイッチの状態が変化した回数を調べます。

変化した回数をもとに、「LED表示」で点灯・点滅・消灯の制御を行います。点滅は 150ミリ秒を経過することに点灯・消灯を切り替えます。

 

「入力待ち」はこんな感じです。

入力ピンの状態を読み込んでスイッチが押されたら state_of_button をセットします。押されなければ何もしません。


フローチャート(入力待ち)

 

スイッチが押されたら「変化を調べる」を実行します。押されていないときは何もしません。

50ミリ秒待ってからスイッチの状態が変化 (ON から OFF、または OFF から ON) しているかを調べます。変化なければ、また50ミリ秒待って調べます。これを 10回、都合 500ミリ秒間行ないます。

スイッチの状態が変化していたら number_of_changes を加算します。変化の回数が 2 になれば「ダブル」が確定しますので、ループを抜けます。これで number_of_changes は「シングル」が 1 、「ダブル」が 2 、「ホールド」は 0 となります。

ちなみに、最後の elapsed_time リセットは LED 点滅周期タイマーのスタートです。


フローチャート(変化を調べる)

 

最後は「LED 表示」です。

number_of_changes 値に従って switch case で分岐します。点滅制御は millis() 関数を利用して経過時間を調べ、150ミリ秒を経過していれば light 値を反転させて点灯・消灯を繰り返すという方法をとります。L チカでよくある delay を使う方法では、プログラムがここで止まってしまうのでうまくありません。


フローチャート(LED表示)

 

 

ということでできたスケッチは以下です。

// button-status.ino 2019/05/31 meyon

 

int inputPin = 2;
int outputPin = 13;
boolean state_of_button = 0;
boolean state_of_currently = 0;
int number_of_changes = 0;
unsigned long elapsed_time;
boolean light = 0;

 

void wait_input(){
  state_of_currently = digitalRead(inputPin);
  if(state_of_currently == 1){
    state_of_button = 1;
    number_of_changes = 0;
  }
  return;
}

 

void detect_changes(){
  if(state_of_button == 1){
    for(int i=0; i<10; i++){
      delay(50);
      state_of_currently = digitalRead(inputPin);
      if(state_of_currently != state_of_button){
        number_of_changes ++;
        if(number_of_changes >= 2){
          break;
        }
        state_of_button = state_of_currently;
      }
    }
    while(state_of_currently == 1){
      state_of_currently = digitalRead(inputPin);
    }
    state_of_button = 0;
    elapsed_time = millis();
  }
  return;
}

 

void light_led(){
  switch(number_of_changes){
    case 0:
      light = 0;
      break;
    case 1:
      light = 1;
      break;
    case 2:
      if(millis() - elapsed_time > 150){
        light = !light;
        elapsed_time = millis();
      }
  }
  digitalWrite(outputPin, light);

  return;
}

 

void setup() {
  pinMode(outputPin, OUTPUT);
  pinMode(inputPin,  INPUT);

 

//  Serial.begin(9600);
}

 

void loop() {
  wait_input();
  detect_changes();
  light_led();

 

//  Serial.print(state_of_currently);
//  Serial.print(state_of_button);
//  Serial.println(number_of_changes);

}

 

コメントアウトしてあるシリアル出力はデバック時に変数の値を確認していたものです。このように適当な位置に Serial.print を入れると、その場所で変数がどうなっているかが見えるようになります。うまく動かないときに重宝しますね。

 

ということで、スイッチアクションで制御を切り替えることができるようになりました。

 


■Ubuntu Server / NAS PC連動電源機能を設定する

設定中の ubuntu server でもバッファローの NAS TS-WXL の PC 連動電源機能を利用できるようにしようと思います。

PC 連動電源機能は PC の off-on に NAS の off-on を連動させる機能です。この機能については「ubuntu 18.04 / NASのPC連動電源機能を使う」で書いていますが、今回は crontab -e ではなく、ディレクトリ cron.d に実行スクリプトを置くように変更しました。

 

スクリプトを naspoweron としてユーザーディレクトリに作成します。cron.d では拡張子のないファイルを実行しますので、拡張子は無しとします。

#! /bin/bash

while true

do

  for i in `seq 3`

  do

    wakeonlan -i 192.168.1.xx -p 9 AA:BB:CC:DD:EE:FF

  done

  sleep 150s

done

 

作成したら実行属性を与えて、動作することを確認しておきましょう。150秒おきにマジックパケットを 3 回送信します。パケットの状況は

 

$ sudo tcpdump udp and host 192.168.1.xx

 

で確認できます。

 

このスクリプトを「Ubuntu Server / 指定時刻に自動シャットダウンする」で作成した /etc/cron.d/user_cron に追加し、起動時に実行させます。

 

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# m h dom mon dow user    command
05 00    * * *    root    shutdown -h +1

@reboot    root    /home/hoge/naspoweron

 

@reboot は起動時に一回だけ実行するという意味で、時間指定部分を置き換えます。

 

cron を再起動します。

 

$ sudo service cron restart


■Ubuntu Server / 指定時刻に自動シャットダウンする

ubuntu server で cron を利用して指定した時刻に自動シャットダウンするようにします。

 

以前は crontab -e で設定していたのですが、今回は cron.d に設定することにします。cron.d ディレクトリにおいた拡張子のないファイルが実行されますので、crontab をコピーして利用することにします。

 

$ sudo cp /etc/crontab /etc/cron.d/user_cron

 

コピーしたファイルを以下のように変更します。

 

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# m h dom mon dow user    command
05 00    * * *    root    shutdown -h +1

 

これで毎日 00:05 にシャットダウンコマンドが起動し、1分後に実際にシャットダウンします。

本当はアクセスしているクライアントがないかどうかを確認する必要があるのでしょうが、まぁ家サーバなので強制的にシャットダウンしてもそんなもんです (^_^;)

 

変更したら再起動します。

 

$ sudo service cron restart

 


■Windows7 / WindowsUpdateでネットワークアダプタが消える

Windows7 機を整備するために Windows Update を実行したら、ネットワークアダプタが消えてしまったという話です。

 

この Windows7 機を最後に更新したのは 3年近く前です。整備するためにまず Windows Update を実行します。

が、その前に、なにかトラブルがあった時のために HDD を丸ごとバックアップしておきます。何でもないことだけどこれは必須です。何度助けられたことか (^_^;)

蛇足ですが。バックアップを取られる人は多いと思いますが、リストアってやったことあります? たとえば別の HDD にリストアしてみるといった予行演習的なことは、常々やっておいたほうが良いです。いざというときに「リストアできない!」「方法がわからない!」という悲惨な目にあわないためにね。

 

閑話休題。

 

Windows Update の設定は「更新プログラムを確認しない」になっていましたので、それはそのまま「更新プログラムの確認」を行いました。いくつかの更新プログラムが表示されましたが、最新のものは 2019/05/14 のマンスリー品質ロールアップなどです。インストールを実行し、再起動の指示に従い再起動。とりあえず問題なく Windows7 は起動しました。

 

が、ネットワークにつながりません。タスクトレイのネットワークアイコンには ×印がついています。

そういえば Windows Update でネットワーク設定が変更されるって記事があったよなぁ、と思い出して、ネットワークアダプタの設定を確認しようとすると…… 無い…… ローカルエリア接続のアイコンが無い (^_^;)

デバイスマネージャを確認すると、ネットワークアダプタは「ドライバがインストールされていません」となっていました。ドライバの更新を実行するとなにやらエラーを起こしてインストールできません。

 

さて、どうするか?

なにかとお世話になっている「ぼくんちのTV別館」で Windows Update の情報を確認すると

今後、Windows7の更新は注意が必要 (再掲)

 

※この項目は、2019年4月度情報の再掲です。
今後、Windows 7 を2018年3月以前のイメージからリカバリを行おうと思った場合や、ゲスト仮想マシンでの運用では注意が必要。Windows Update 適用後に、ネットワーク設定が意図しないものに変更される場合がある。

とありました。

 

これかぁ、これに違いない。なんてったって最終更新は 3年前だもの (^_^;)

 

こうなってしまったら更新プログラムの削除とかシステムの復元とかを考えるわけですが、そんなことして深みにはまるより、さっさとバックアップからリストアして元に戻したほうが早いです。もうね、神様、仏様、バックアップ様ですよ。

というわけでリストア完了。もう一度 Windows Update をやり直します。

 

更新プログラムの確認をして、まず 2019年4月10日より前の日付の更新プログラムだけをインストールします。

次に、更新プログラムの確認をして表示された 5月分の更新プログラムをすべて非表示にし、もう一度更新プログラムの確認を行います。すると 4月分の更新プログラムが表示されますので、これも非表示にします。

さらに更新プログラムの確認を行うと、今度は 3月分のマンスリー品質ロールアップ (KB4489878) を含む 3月分の更新プログラムが表示されます。これをインストールします。

この「2019-03 マンスリー品質ロールアップ (KB4489878) 」こそが必須の更新プログラムなのだそうです。

 

引き続き更新プログラムの確認を進めます。

もう一つ必須の「サービススタック更新プログラム (KB4490628) 」が表示されますので、インストール。さらに更新プログラムの確認を行うと「利用できる重要な更新プログラムはありません」となりました。

ここで非表示の更新プログラムの再表示を行うと 5月分の更新プログラムが表示されます。たぶんもうこれをインストールしても問題ないのだろうと思いますが、念のためこれをもう一度非表示にして、4月分の更新プログラムを表示させインストールします。

そして最後に、5月分の更新プログラムをインストール。

 

無事、ネットワークアダプタには被害なく、Windows Update を完了しました。

 


<< | 3/61PAGES | >>

■calendar

S M T W T F S
      1
2345678
9101112131415
16171819202122
23242526272829
<< February 2020 >>

■search this site.

■recommend

毎日貯まるポイントサイト ECナビ

■recommend

* 楽天ROOM *

■Twitter

■recommend

■recommend

■selected entries

■categories

■archives

■recent comment

■recent trackback

■links

■profile

■others

■mobile

qrcode

■powered

無料ブログ作成サービス JUGEM