本来はイーサネットシールドを利用してインターネットに接続するのだそうですが、今回は Arduino をパソコンに USB 接続した状態で制御させます。
参考にさせていただいたサイトは以下です。ありがとうございました。
1. Blynk アプリの設定
俺は Android なので、Play ストアから Blynk アプリをインストール。
起動したらまずアカウントを作成します。
次に新しいプロジェクトを作ります。デバイスは「Arduino Nano」、接続タイプは「USB」としておきます。プロジェクトを作るとアカウントのメールアドレスへトークンが送られてきます。このトークンが制御する Arduino を識別することになります。
ウィジェットからボタンを選択し、アウトプットを「Digital D13」、モードを「SWITCH」とします。
2. Arduino の設定
GitHUB から blynk-library-master.zip をダウンロードし、IDE の「.ZIP 形式のライブラリをインストール」で取り込みます。
サンプルスケッチは /Arduino/libraries/blynk-library-master/examples/Boards_USB_Serial/ にある「Arduino_Serial_USB」です。この中の「YourAuthToken」を送られてきたトークンに書き換えて、Arduino に書き込みます。
3. パソコンの設定
USB 接続で Arduino を制御するために blynk-ser.sh を起動させる必要があります。blynk-ser.sh は /Arduino/libraries/blynk-library-master/scripts にあります。
まずこれに実行権限を与えます。
$ chmod +x blynk-ser.sh
実行してみます。
$ ./blynk-ser.sh
This script uses socat utility, but could not find it.
Try installing it using: sudo apt-get install socat
socat がないぞと叱られました。インストールしましょう。
$ sudo apt update
$ sudo apt install socat
もう一度 blynk-ser.sh を実行してみましょう。
$ ./blynk-ser.sh
Resetting device /dev/ttyUSB0...
[ Press Ctrl+C to exit ]
Connecting: FILE:/dev/ttyUSB0, ........:
:
この状態でスマホ側の Blynk アプリからプロジェクトを起動すると、ボタン操作で Arduino の内臓 LED を点滅することができました。
blynk-ser.sh は Ctrl+C で中断できます。Arduino のスケッチを修正する場合などは中断して、USB を開放する必要があります。
ほんとの基本的なものでしたが、スマホから制御できるようになると一段と IoT 機器らしくなってきますね。
]]>
TUTORIALS > Built-In Examples > 02.Digital > StateChangeDetection
フローチャートを書いてみました。
詳細な解説はしませんが、ボタンの状態によって分岐する流れが赤線のようになっています。
スケッチを書き込んで起動すると、最初は点灯します。ボタンを押すと消灯し、4回目の押下で再び点灯します。
このスケッチでは、チャタリング防止 (デバウンス) は delay(50) で行なっています。前回の「Debounce」のような方法との使い分けってどうすれば良いのでしょう? 暇なときにでも考えてみます (^_^;)
]]>
TUTORIALS > Built-In Examples > 02.Digital > Debounce
Debounce
Pushbuttons often generate spurious open/close transitions when pressed, due to mechanical and physical issues: these transitions may be read as multiple presses in a very short time fooling the program. This example demonstrates how to debounce an input, which means checking twice in a short period of time to make sure the pushbutton is definitely pressed. Without debouncing, pressing the button once may cause unpredictable results. This sketch uses the
millis()
function to keep track of the time passed since the button was pressed.
2番ピンに接続されたスイッチを押すたびに 内蔵 LED が点灯と消灯に切り替わるというものですが、この場合に問題となるスイッチのチャタリングを防止するためのスケッチ例です。
スケッチをパッと見てもどうなっているのかよくわかりませんでしたので、フローチャートを書いてみました。
赤い部分がデバウンスを行なっています。
ボタンの状態が変化したことを検出したら lastDebounceTime をリセットし、debounceDelay (50ms) の間は LED 出力を変化させません。ボタンの状態が変化なく 50ms 経過したら LED の制御処理を実行しますが、変化があったときは再び lastDebounceTime をリセットし、変化がなくなるのを待っています。
こうしてデバウンス部分を分けてみると、とても単純な処理をしていることがわかりますね。
ちなみに、このスケッチ例では ledState を反転させるために「!」でブーリアン値の否定を利用しています。俺の好きな XOR によるビット操作 ledState ^= 1 よりわかりやすいです (^_^;)
]]>
TUTORIALS > Built-In Examples > 02.Digital > BlinkWithoutDelay
回路図では外部に LED を接続するようになっていますが、内蔵 LED を点滅させるので必要はありません。スケッチ例をそのまま IDE にコピーアンドペーストし、Arduino へ書き込めば完成ですね。
スケッチ例はコメントが多くて見にくいので、必要な部分だけ抜き出してみました。
const int ledPin = LED_BUILTIN;
int ledState = LOW;
unsigned long previousMillis = 0;
const long interval = 1000;
void setup() {
pinMode(ledPin, OUTPUT);
}
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
if (ledState == LOW) {
ledState = HIGH;
} else {
ledState = LOW;
}
digitalWrite(ledPin, ledState);
}
}
delay を使わずに、millis() でインターバル時間を超えたかどうかをチェックする方法ですね。
俺が気になったのは、LED の点滅のために ledState を反転させる部分 (16〜20行) です。if() で判定するのはとってもわかりやすいですが、別の方法に変更してみましょう。
ledState = ~ledState;
ledState を NOT 演算で反転させてみました。この場合は LOW=0 、HIGH=-1 となります。ビットがすべて反転しているということ。LED の点滅は正常に行われていますので、たぶん 1 ビット目の値で制御しているのでしょう。
NOT はビット単位の否定ですが、次の例はブーリアン値で反転させます。
ledState = !ledState;
出力は 0 と 1 になりました。この場合は、変数をブール型にして値は true - false とするべきなのでしょうね。
次の例は、1 ビット目だけを反転させる方法です。XOR 演算でビット操作しています。
ledState = ledState^1;
これは複合演算子を使って、
ledState ^= 1;
のように書くことができます。個人的にはこの方法が好きですが、理論的にどれがよいのかは、俺にはわかりません (^_^;)
]]>
参考にさせていただいたサイトは「ubuntu 環境の Python」です。
linuxBean 16.04 に入っている Python のバージョンは、2系が 2.7.12 、3系が 3.5.2 です。最新バージョンは 3.7.4 で、これをインストールしてみましょう。
1. ビルドツール・ライブラリのインストール
$ sudo apt update $ sudo apt install build-essential libbz2-dev libdb-dev ¥ libreadline-dev libffi-dev libgdbm-dev liblzma-dev ¥ libncursesw5-dev libsqlite3-dev libssl-dev ¥ zlib1g-dev uuid-dev tk-dev
必要なツールとライブラリの準備です。個々の内容はわかりません (^_^;) build-essential はインストール済みだと思うけど、まぁコピー&ペーストでそのまま実行です。
GUI では、synaptic パッケージマネージャからインストールできると思います。
2. ソースコードのダウンロード
Gzipped source tarball から Python-3.7.4.tgz をダウンロードし、展開します。
$ tar xzf Python-3.7.2.tgz
コマンドでもいいんですが、ブラウザでダウンロードしたらダウンロードフォルダを開いて、アーカイブマネージャで展開するのが簡単ですよ。展開してできたフォルダ Python-3.7.4 に入ったら、「現在のフォルダを端末で開く」すれば次のコマンドの「cd Python-3.7.4」まで完了です。
3. ビルド
ビルドとインストールはコマンドで。
$ cd Python-3.7.4 $ ./configure --enable-shared $ make $ sudo make install $ sudo sh -c "echo '/usr/local/lib' > /etc/ld.so.conf.d/custom_python3.conf" $ sudo ldconfig
バージョンの確認は以下です。
$ python3 --version
Python 3.7.4
ということで、無事 3.7.4 にアップデートできました。
]]>
送られてくるのは文字列なので、そいつを数値に変換するためにいろいろ細工が必要になる。でもきっと何か、もっと簡単な方法があるはずだと思っていたわけですが、なんと、あるじゃないですか、数値をそのまま取り出してくれる関数が。
parseInt()
Description
parseInt() returns the first valid (long) integer number from the serial buffer. Characters that are not integers (or the minus sign) are skipped.
しかも附属のスケッチ例にこんなのがあります。
Read ASCII String
This sketch uses the Serial.parseInt() function to locate values separated by a non-alphanumeric character. Often people use a comma to indicate different pieces of information (this format is commonly referred to as comma-separated-values or CSV), but other characters like a space or a period will work too. The values are parsed into integers and used to determine the color of a RGB LED. You'll use the Arduino Software (IDE) serial monitor to send strings like "5,220,70" to the board to change the light color.
/*
Reading a serial ASCII-encoded string.
This sketch demonstrates the Serial parseInt() function.
It looks for an ASCII string of comma-separated values.
It parses them into ints, and uses those to fade an RGB LED.
Circuit: Common-Cathode RGB LED wired like so:
- red anode: digital pin 3
- green anode: digital pin 5
- blue anode: digital pin 6
- cathode: GND
created 13 Apr 2012
by Tom Igoe
modified 14 Mar 2016
by Arturo Guadalupi
This example code is in the public domain.
*/
// pins for the LEDs:
const int redPin = 3;
const int greenPin = 5;
const int bluePin = 6;
void setup() {
// initialize serial:
Serial.begin(9600);
// make the pins outputs:
pinMode(redPin, OUTPUT);
pinMode(greenPin, OUTPUT);
pinMode(bluePin, OUTPUT);
}
void loop() {
// if there's any serial available, read it:
while (Serial.available() > 0) {
// look for the next valid integer in the incoming serial stream:
int red = Serial.parseInt();
// do it again:
int green = Serial.parseInt();
// do it again:
int blue = Serial.parseInt();
// look for the newline. That's the end of your sentence:
if (Serial.read() == '¥n') {
// constrain the values to 0 - 255 and invert
// if you're using a common-cathode LED, just use "constrain(color, 0, 255);"
red = 255 - constrain(red, 0, 255);
green = 255 - constrain(green, 0, 255);
blue = 255 - constrain(blue, 0, 255);
// fade the red, green, and blue legs of the LED:
analogWrite(redPin, red);
analogWrite(greenPin, green);
analogWrite(bluePin, blue);
// print the three numbers in one string as hexadecimal:
Serial.print(red, HEX);
Serial.print(green, HEX);
Serial.println(blue, HEX);
}
}
}
スケッチ例ではカラー LED を使用していますが、点灯を試すだけなら 3個の LED を使えばいいです。シリアルモニターから 3つの数値をカンマ区切りで送信すると、その値に応じた色 (明るさ) で LED が点灯します。
シリアルポートから送られてくるデータが文字列だの数値だの考えなくても、そのまま数値として出力できました。
なお、Serial.parseInt() はタイムアウトで終了しますので、cu のように入力した数値を即送信してしまう場合は、タイムアウトするまでにすべてのデータを送信する必要があります。シリアルモニターのようにデータをまとめて送信するものであれば問題はないです。
プログラムからなら、データをまとめて最後に改行コードをつけて送信すればよいので、なんとでもなると思います。
ということで、シリアル通信で数値を送ることも難しく考える必要なんてないことがわかりました。やっぱりなぁ、日本語リファレンスだけじゃなくて、ちゃんと REFERENCE を読まないといけないんだよなぁ (^_^;)
]]>
前回は数字を 3つ入力すると LED の明るさが変わるというものでした。これを 0 から 255 までの数字を入力して Enter を押すと、その値に従って LED の明るさを変えるようにしたいと思います。
そのために、桁数の異なる数字をシリアル通信で Arduino に送り、数値として PWM 制御に渡す必要があります。少しググってみたのですが、シリアル通信で数値を送るには、文字列を数値に替えるだけでなく、桁数の違いをどう処理するかなど、けっこう面倒な雰囲気です。単純な処理なのでもっと簡単な方法がないかなと探していたところ、こんなチュートリアルを見つけました。
The toInt() function allows you to convert a String to an integer number.
In this example, the board reads a serial input string until it sees a newline, then converts the string to a number if the characters are digits.
ドンピシャです。文字列を数値に変換する、まさに求めていた内容ですよ。
toInt() は String オブジェクトの関数です… よくわかりません (^_^;) でもこうやって使うとこういう結果が出るということは、簡単に理解できます。なお、このチュートリアルにあるサンプルコードは、IDE のスケッチ例「08.String」に「StringToint」として附属しています。
さて、そのサンプルコードを参考にした、cu から送信するデータに従って 3番ピンに接続した LED を PWM 制御するスケッチです。
// PWM control LED from cu 2019/09/09 meyon
const int ledPin = 3;
String inStr = "";
void setup()
{
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
}
void loop()
{
while(0 < Serial.available()) {
int inChar = Serial.read();
Serial.write(inChar);
if(isDigit(inChar)) {
inStr += (char)inChar;
}
if(13 == inChar) {
int ledBri = inStr.toInt();
ledBri = constrain(ledBri, 0, 255);
analogWrite(ledPin, ledBri);
Serial.print("¥nBrightness: ");
Serial.println(ledBri);
inStr = "";
}
}
}
16行目は cu へのエコーです。Serial.write() はデータを byte 型で出力するので、受け取った数字の ASCII コードをそのまま cu へ送り返し、入力した数字を表示させることができます。
18行目で入力データが数字かどうかを判断し、数字なら文字列として inStr に追加していきます。
22〜25行目。入力データが Enter の場合 ASCII コード (13) が届きますので、それまでに届いた文字列を数値に変換して 3番ピンへアナログ出力します。
数値は 0〜255 の範囲に制限し、256 以上の場合は 255 としています。int 型なので 32767 を超えると出力は 0 になってしまいますね。また、入力に数字以外が含まれていると無視されます。数字だけを抽出して制御値になります。
これでかなりエクセレントになった気がします (^_^;)
]]>
ubuntu では cu コマンドを利用します。まずはインストール。
$ sudo apt install cu
起動・接続は以下。切断・終了は「~.」ですが、Arduino で受信したデータを送り返すスケッチを動かしていると ~. も折り返されてくるだけで終了できません (^_^;) 正しい方法かどうかわからないのですが、Ctrl+c 押した後に ~. で終了できました。
$ cu -s 9600 -l /dev/ttyACM0
シリアルモニタとは動作が異なります。
シリアルモニタでは入力した文字列が「送信」ボタンを押すことで一括して送られましたが、cu では入力ごとに即送信され、たとえば「A」キーを押すとすぐに「65」が返ってきます。
そこで、数字を 3つ入力するとその値で LED の明るさを制御するようなスケッチを書いてみました。
LED は 3番ピンに繋ぎますが、最大で 20mA 程度流しますので LED 駆動用にトランジスタを 1個入れてます。まぁ毎度の回路です。電源は USB を刺しているので、Arduino の 5V 出力を利用しました。
const int ledPin=3;
void setup() {
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
}
void loop() {
while(0 < Serial.available()) {
int data = Serial.read();
Serial.println(data);
static int val[3];
static int i=0;
val[i]=data-48;
i++;
if(2<i) {
int bri = val[0]*100+val[1]*10+val[2];
bri = constrain(bri, 0, 255);
Serial.print("Brightness: ");
Serial.println(bri);
analogWrite(ledPin, bri);
i=0;
}
}
}
Arduino へ書き込んだら IDE を終了します。
cu を起動し接続。数字を 3つ押すごとに LED への出力が変化します。「255」 と押すと LED が 100% で点灯します。「128」なら 50% 、「000」で消灯です。入力値が 255 を超える場合は 255 にしますが、contrain() なんて便利な関数があったので使ってみました。
受信したデータは 48 を引くことで数値に変換しています。3つデータを受けたら 3桁の数字に計算しているだけですが、このあたりがどうもエクセレントじゃないですよねぇ (^_^;)
]]>
では、シリアルモニタから Arduino へ送信する場合はどうでしょうか。簡単なスケッチで試してみます。
void setup() {
Serial.begin(9600);
}
void loop() {
if(Serial.available() > 0) {
int data = Serial.read();
Serial.println(data, DEC);
}
}
シリアルモニタから「A」と送信すると、
65
10
と表示されました。
シリアルモニタ側で「A」と入力すると、文字「A」の ASCII コード (65) がシリアルポートへ送られるはずです。
Arduino 側では、Serial.read() で読み取ったデータを数値変数 data に代入し、Serial.println() で 10 進数のフォーマットで出力しています。そこで表示されたのが「65」ということは、Serial.read() がシリアルポートから送られてきた ASCII コードそのものを読み取っている、ということです。
次に表示されている「10」は、シリアルモニタから送信されるときに付加された改行コード LF のASCII コードです。シリアルモニタでは、入力されたデータは「送信」ボタンを押したときに改行コードを付加してまとめて送出されています。
ということはですよ、入力した文字をそのまま表示させるには、読み取った ASCII コードを文字に変換すればいいんじゃないですか?
char data = Serial.read();
Serial.println(data);
スケッチの一部を上のように書き換えます。シリアルモニタで「A」と入力すると、
A
と出力されました。ASCII コード (65) を文字変数 data に代入すると、その中身は文字「A」となりますね。そーゆーことです (^_^;)
ところで、「ABC」と入力するとどうなりますか?
A
B
C
となってしまいます。スケッチをちょっと工夫しましょう。
void loop() {
if(Serial.available() > 0) {
char data = Serial.read();
if(10 == data) {
data = '¥n';
}
Serial.print(data);
}
}
末尾に改行コード LF がついてくるのですから、LF のときだけ改行するようにしました。これで出力は、
ABC
となりました。
これでシリアル通信の状況がわかりました。わかってしまえば、まぁなんてことないですよねぇ (^_^;) たぶん、ね。
]]>
Reference の Serial.print() を確認してみました。
Prints data to the serial port as human-readable ASCII text. This command can take many forms. Numbers are printed using an ASCII character for each digit. Floats are similarly printed as ASCII digits, defaulting to two decimal places. Bytes are sent as a single character. Characters and strings are sent as is.
データは ASCII コードで出力されます。数字も各桁ごとに ASCII コードになるようですね。浮動小数点数も同じですが、小数点以下 2桁になるとのこと。
バイト形式は一つの文字として送るということなので、この場合は数字のようにバラバラにはならないということでしょうか。あとで確認してみましょう。
文字と文字列は「そのまま」です。
では、実際に確認してみます。ちなみに Serial.println() はデータの末尾に改行コード (CR+LF) を付加する関数で、それ以外は Serial.print() と同じです。
文字列を送ってみます。
Serial.println("hoge");
シリアルモニタには「hoge」とそのまま表示されます。内部的には ASCII コードで送るのでしょうが、文字形式は「そのまま」の文字として出力されるということです。
変数ではどうでしょうか。
char str[]="Hello world!";
Serial.println(str);
変数の場合も意図したように、そのまま「Hello world!」と表示されました。
数字形式で試してみます。
int num=65;
Serial.println(num);
シリアルモニタには「65」と表示されました。この場合は「65」が文字列としてそのまま送信されたということなのでしょう。
浮動小数点数を送ってみましょう。
float num=12.345678;
Serial.println(num);
「12.34」と小数点以下 2桁になって表示されました。
数字では表示フォーマットを指定できます。
int num=65;
Serial.println(num, BIN);
この場合は「1000001」と 2進数形式で表示されます。あくまでも文字列として扱っているのですから、BIN を指定した場合は改行コードを含んで 9 バイト送信しているということになるのでしょうか。
なお、浮動小数点数の場合はフォーマットで小数点以下の桁数を指定できるようです。
バイト形式を試してみましょう。バイト形式はひとつの文字として送るということでした。
byte b=B01000001;
Serial.println(b);
「65」と表示されました。B01000001 は 10 進数の「65」を表していますので、文字列「65」を送信したってことですね。
では、これはどうでしょうか。
byte b='A';
Serial.println(b);
文字「A」をバイト形式にして送信すると、シリアルモニタには「65」と表示されました。「A」の ASCII コードの値「65」が送信されたということですね。
フォーマットを指定してみます。
byte b='A';
Serial.println(b, BIN);
今度は「1000001」と表示されました。「65」が 2進数形式で送信されたということです。
さてと、こうしてみてくるとなんだか様子がわかってきました。Serial.print() 、Serial.println() では、
ってことです。わかってしまえば当たり前のことですけど、どうやら俺は、数字と ASCII コードを混同してわけわからなくなってしまうようです。
次はもう一度、シリアルモニタから Arduino へ送る場合を試してみたいと思います。
]]>
まず、Reference の Serial.available() にあるサンプルスケッチで試してみましょう。
int incomingByte = 0; // for incoming serial data
void setup() {
Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
}
void loop() {
// reply only when you receive data:
if (Serial.available() > 0) {
// read the incoming byte:
incomingByte = Serial.read();
// say what you got:
Serial.print("I received: ");
Serial.println(incomingByte, DEC);
}
}
シリアルインターフェースから受信したデータをそのまま送り返すスケッチです。シリアルモニタを開いて試してみました。
入力 | 出力 |
123 | I received: 49 I received: 50 I received: 51 I received: 10 |
ABC | I received: 65 I received: 66 I received: 67 I received: 10 |
シリアルモニタから「123」と入力して送信すると、出力のように表示されます。ここで、あれ?と思っちゃう。そのまま送り返しているはずなのに、違うじゃん (^_^;)
よーく考えてみると、文字「1」の ASCII コードは「49」、文字「2」は「50」です。入力したデータが ASCII コードとしてやり取りされている、ということですね。
最後の「10」は改行コードの LF を表します。シリアルモニタでは送信ボタンをクリックすると LF が送られるんです。シリアルモニタの右下の「LFのみ」を「CRのみ」とすると改行コード CR の「13」が表示されます。
シリアルモニタから入力された文字列は Arduino に送られてシリアルバッファに格納されます。Serial.read() を実行するとバッファの最初の 1 バイトが読み取られます。そして最初の 1 バイトはクリアされる (のじゃないかと思います) 。Serial.read() を繰り返すたびに 1 文字ずつ読み取っていき、読み取れるデータがなくなったら次のデータを待ちます。
そんな仕組み。間違っているかもしれません。間違っていたら笑ってやってください。
ところで、これを文字列にしようと思ったら 1 文字ずつ配列変数に格納するなどしなくてはいけません。Serial.available() で文字数を取得して配列変数を定義し、1 文字ずつそこに格納していくって感じかな。難しくはないけれど、面倒くさい (^_^;)
文字列を簡単に読み取れないのかなぁとググってみたら、Serial.readString() という関数があることがわかりました。ただ、この関数はタイムアウト (デフォルトは 1000ms) で終了するので、処理が遅いのが欠点。実際試してみると、データを送信して 1 秒後に表示されます。
そこで、Serial.readStringUntil( terminator ) 関数を使うことにしました。terminator に '¥n' を指定すると、改行コードを受信した時点で終了してくれます。
作り直したスケッチはこんな感じ。変数名やコメントは変えてないので、ちょっと違和感もありますが、ご容赦。
String incomingByte; // for incoming serial data
void setup() {
Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
}
void loop() {
// reply only when you receive data:
if (Serial.available() > 0) {
// read the incoming byte:
incomingByte = Serial.readStringUntil('¥n');
// say what you got:
Serial.print("I received: ");
Serial.println(incomingByte);
}
}
これで入力した文字列と同じ文字列が出力に表示されるようになりました。
]]>
AtCoder Beginner Contest 138 A - Red or Not
問題文
整数 a と、英小文字からなる文字列 s が入力されます。
a が 3200 以上なら s と出力し、 a が 3200 未満なら
red
と出力するプログラムを書いてください。
難しいことは何もないのですが、いざソースを書こうとしたら文字列の扱い方がわからない (^_^;)
文字列は配列に入れる。文字数が10文字ならば最後にヌルを付けて11文字分必要になるので、
char s[11];
scanf("%s", s);
とする。間違えて s[10] としてもヌルの分は自動的に足してくれるらしい。s[] としておけば必要なだけ割り当ててくれる。
代入するときは s[] じゃなくて s だけ書く。これはポインタを示しているらしい。
出力は同様に、
printf("%s¥n", s);
とすれば、文字列として出力される。
printf("%c¥n", s[2]);
のようにすると、3 文字目が出力される。
まぁそんな感じかな。
ってことでこんなふうになりました。
#include <stdio.h>
int main()
{
int a;
char s[11];
scanf("%d %s", &a, s);
if (3200<=a) {
printf("%s¥n", s);
} else {
printf("red¥n");
}
return 0;
}
なお、ソースコードの表示には「srctohtml ソースをHTMLで見やすく出力するツール」を利用させていただきました。ありがとうございます。
]]>
使っていない CPU 冷却ファンがあったので、これを回してみましょう。
ファンから出ている線は 4 芯です。ググってみると、4 芯のものは PWM で回転数制御ができるんだとか。電源に 12V を与えておき、control に 25KHz の PWM 信号を入れてやるとそのデューティー比によって回転数が変化する。また、sense には 1 回転あたり 2 パルス (デューティー比 50%) の信号がオープンコレクタで出てくる。らしい。
このファン自体の仕様はわからなかったのですが、汎用品のようですのでたぶん同じでしょう。
ということで、回路図。Arduino Nano 互換品を使いました。
ジャンクのサーミスタがあったので、これで温度を検出し回転数を制御することにします。
このサーミスタはレーザープリンタの定着部の部品で 200℃ 程度の計測がベストなんですけど、まぁテキトーに使いましょ。この定数での入力値は、約 26℃ で 830 、約 32℃ で 780 ぐらいになりました。
Arduino の Vin に 12V を入れていますが、USB をつないだまま 12V を落としたときに 5V が逆流するのを防ぐためにダイオードを入れています。実験中は電源を切ることがよくありますのでね。
control は 3.3V 推奨なので、ツェナーダイオードで降圧しています。
control の電流の実測値は吐き出しで約 0.6mA でした。Arduino の 3 番ピンが HIGH (5V) のとき、ツェナーダイオードには R2 を介して約 11mA 流れ、control 側の HIGH レベルは 3.3V になります。3 番ピンが LOW (0V) のとき R2 の電圧降下は 0.09V ですので、control 側は問題なく LOW レベルに落ちます。
sense はオープンコレクタなのでプルアップしています。内部プルアップでも構わないのですが、プルアップを明示的にするために抵抗を入れることにしています。まぁ好みです。
さて、問題の PWM ですが、Arduino の PWM は 490Hz か 980Hz ですのでこのままでは使えません。そこで Arduino で 25KHz PWM を作る方法をググってみると、けっこうたくさんヒットします。なるほどっポンと膝を叩いた、参考にさせていただいたサイトはこちらです。ありがとうございます。
そしてできあがったスケッチは以下。
/* FAN Controller 2019.08.15 meyon
*
* Timer2 Mode5 PhaseCorrect PWM
* Clock 16MHz / 8 = 2MHz
* PWM countuency 25KHz
* OCR2A = 2MHz / 25KHz / 2 = 40
* OCR2B = dutyRatio ratio 0 ~ 40
* PWM output PD3
*/
volatile long riseTime = millis();
volatile long prevTime = millis();
void setup()
{
pinMode(3, OUTPUT);
TCCR2A = _BV(COM2B1) | _BV(WGM20);
TCCR2B = _BV(WGM22) | _BV(CS21);
OCR2A = 40;
OCR2B = 0;
pinMode(2, INPUT);
attachInterrupt(digitalPinToInterrupt(2), sense, RISING);
Serial.begin(115200);
}
void loop()
{
static const int thermMax = 780;
static const int thermMin = 830;
static const float gradients = 40.0/(thermMax-thermMin);
int thermValue = analogRead(A0);
float dutyRatio = (thermValue-thermMin)*gradients;if (0>dutyRatio) {
dutyRatio=0;
} else if (40<dutyRatio) {
dutyRatio=40;
}
OCR2B = (int)dutyRatio;
long cycle=riseTime-prevTime;
long rpm=30000/cycle;
if (100<millis()-riseTime) {
rpm=0;
}
// data monitor
Serial.print(thermValue);
Serial.print("¥t");
Serial.print(dutyRatio);
Serial.print("¥t");
Serial.print(rpm);
Serial.print("¥n");}
void sense()
{
prevTime=riseTime;
riseTime=millis();
}
setup() のなかで PWM の動作を規定しています。
WGM20 、WGM22 をセットすることで Mode 5「PWM Phase Correct」になります。COM2B1 はデジタルピン 3 番の PWM 出力を規定します。CS21 は分周比を 8 としています。OCR2A で TOP を 40 にすることで、出力周波数が 16000 / ( 8 * 40 * 2 ) = 25KHz になります。
OCR2B は 3 番ピンの比較レジスタで、これを 0 〜 40 にすることでデューティー比 0 〜 100% の PWM を発生させることができます。
サーミスタ入力を 830 〜 780 とし、その範囲でデューティー比を 0 〜 40 (0 〜 100%) として OCR2B を設定します。
2 番ピンは sense を入力し、パルスの立ち上がりで関数 sense() を呼び出しています。これによりパルスの周期を計測して回転数を算出しますが、100msを超えて割り込みが無いときは回転数を 0 としています。
ちなみに、ビットアクセスマクロ _BV() は、指定したビットをセットし、他をクリアします。| でつなぐことで複数ビットをセットしています。
指定したビットのみセットするには、たとえば DDRD |= _BV(PD3) のようにします。これは DDRD = DDRD | _BV(PD3) ということですね。PD3 のビットのみセットし、他は変更しません。 指定したビットのみクリアするには DDRD &= ~_BV(PD3) とします。DDRD = DDRD & ~_BV(PD3) ということです。
]]>
余談ですが、いろいろ数値を入力して確認しているときに、答えが負数になる場合がでてきました。確認のために AC の方のプログラムを実行してみたところ、やっぱり負数になります。うーん、マイナスの雨が降るなんてありえない話じゃ ……
考えること小一時間 (^_^;) 問題文の制約に次の一文を見つけました。
入力が表す状況は、各山に非負の偶数リットルの雨が降った際に発生しうる。
わかりにくい文ですが、つまり「答えが負数になるような入力は与えませんよ」ってことなんでしょう。ということで、それは気にする必要はないのだと判断しました。
閑話休題
結果が TLE になったということは計算量が多すぎるってこと。解決策を求めて解説をみてみることにします。ん〜と… そうか、漸化式をそのまま順番に計算していけば O(n) にできるんだ。なるほど (^_^;)
では解説を参考にして、もう一度考えなおしてみましょう。
前回と同じく山は 5 個とします。降雨量の総量を S とすると、
S = A1 + A2 + A3 + A4 + A5 = X1 + X2 + X3 + X4 + X5
X1 = S - ( X2 + X3 + X4 + X5 )
X2 + X3 = 2 × A2 、X4 + X5 = 2 × A4 ですから、
X1 = S - 2 × ( A2 + A4 )
これでまず X1 が求まりました。あとは、
X2 = 2 × A1 - X1
X3 = 2 × A2 - X2
X4 = 2 × A3 - X3
X5 = 2 × A4 - X4
と順番に計算していけばよいです。このような前の式によって定まる式を「漸化式」というそうです。漸化式をそのまま順番に計算していくことで全体の計算量を減らせるなんて、考えが及びませんでした。
ということで、計算部分を改善してみました。太字が変更した部分です。
/* AtCoder Beginner Contest 133 D - Rain Flows into Dams
2019.07.28 by meyon */
#include <stdio.h>
#include <stdlib.h>
void err()
{
printf("Error!¥n");
exit(1);
}
int main()
{
// input N
int N;
scanf("%d", &N);
if(3>N || 99999<N || 0==N%2) {
err();
}
// input A
int A[N];
for(int i=0; i<N; i++) {
scanf("%d", &A[i]);
if(0>A[i] || 1000000000<A[i]) {
err();
}
}
// rainfall X
int X[N];
for(int i=0; i<N; i++) {
X[i]=0;
}
int S=0;
for(int i=0; i<N; i++) {
S += A[i];
}
int P=0;
for(int i=1; i<N; i+=2) {
P += A[i];
}
X[0]=S-2*P; // X1=S-2(A2+A4+...+A[n-1])
for(int i=0; i<N-1; i++) {
X[i+1]=2*A[i]-X[i]; // X[i+1]=2Ai-Xi
}
// display answer
for(int i=0; i<N; i++) {
printf("%d ", X[i]);
}
printf("¥n");return 0;
}
入力して、範囲外の場合はエラー終了。ここは毎度同じです。
改善した rainfall X の部分。
といった感じです。
はい、これで AC (合格) となりました \(^o^)/ 実行時間は 23ms 、メモリ 2048KB でした。
]]>
AtCoder Beginner Contest 133 D 問題 は Rain Flows into Dams です。
問題文
円形に N 個の山が連なっており、時計回りに山 1 , 山 2 , …… , 山 N と呼ばれます。 N は奇数です。
これらの山の間に N 個のダムがあり、ダム 1 , ダム 2 , …… , ダム N と呼ばれます。ダム i ( 1≤i≤N ) は山 i と山 i+1 の間にあります (山 N+1 は山 1 のことを指します)。
山 i ( 1≤i≤N ) に 2x リットルの雨が降ると、ダム i−1 とダム i にそれぞれ x リットルずつ水が溜まります (ダム 0 はダム N のことを指します)。
ある日、各山に非負の偶数リットルの雨が降りました。
その結果、ダム i ( 1≤i≤N ) には合計で Ai リットルの水が溜まりました。
各山に降った雨の量を求めてください。この問題の制約下では解が一意に定まることが証明できます。
さて頭が痛くなりそうですが (^_^;) まずは問題を理解しなければなりません。紙に山とダム (谷) を描いて考えてみましょう。
山が 3 個あるとします。ダム 1 の左が山 1 、右が山 2 です。ダム 1 に溜まった水 A1 は、山 1 と山 2 に降った雨 X1、X2 の半分です。
A1 = ( X1 + X2 ) ÷ 2
したがって山 1 に降った雨 X1 は、
X1 = 2 × A1 - X2
となります。同様にそれぞれの山に降った雨は、
X2 = 2 × A2 - X3
X3 = 2 × A3 - X1
です。ということで、それぞれの式を代入して X を求めると、
X1 = A1 - A2 + A3
X2 = A2 - A3 + A1
X3 = A3 - A1 + A2
となりました。
山が 5 個の場合はどうでしょう。同様に考えていくと
X1 = A1 - A2 + A3 - A4 + A5
X2 = A2 - A3 + A4 - A5 + A1
X3 = A3 - A4 + A5 - A1 + A2
X4 = A4 - A5 + A1 - A2 + A3
X5 = A5 - A1 + A2 - A3 + A4
となります。
さてプログラムですが、この式をどうしましょうか。
for ( i = 0 ; i < N ; i++ ) {
for ( j = i ; j < N+i ; j++ ) {
A[ j%N ];
って感じでループすれば、
A[0] A[1] A[2] A[3] A[4]
A[1] A[2] A[3] A[4] A[0]
A[2] A[3] A[4] A[0] A[1]
A[3] A[4] A[0] A[1] A[2]
A[4] A[0] A[1] A[2] A[3]
という数列が作れます。あとは符号を反転しては加算していく、と。うん、できそうですね。
ということで、
という流れでプログラムを書いてみました。
/* AtCoder Beginner Contest 133 D - Rain Flows into Dams
2019.07.26 by meyon */
#include <stdio.h>
#include <stdlib.h>
void err()
{
printf("Error!¥n");
exit(1);
}
int main()
{
// input N
int N;
scanf("%d", &N);
if(3>N || 99999<N || 0==N%2) {
err();
}
// input A
int A[N];
for(int i=0; i<N; i++) {
scanf("%d", &A[i]);
if(0>A[i] || 1000000000<A[i]) {
err();
}
}
// rainfall X
int X[N];
for(int i=0; i<N; i++) {
X[i]=0;
}
for(int i=0; i<N; i++) {
int s=-1;
for(int j=i; j<N+i; j++) {
int amou=A[j%N];
s=-s;
X[i] += s*amou;
}
}
// display answer
for(int i=0; i<N; i++) {
printf("%d ", X[i]);
}
printf("¥n");
return 0;
}
さぁ提出してみましょう。…… あれ? … あー、TLE (実行時間制限超過) になってしまいました (*_*;)
]]>
競技プログラミング AtCoder Beginner Contest 133 C - Remainder Minimization 2019 を解いてみます。
問題文
非負整数 L,R が与えられます。 2 つの整数 i,j を L≤i<j≤R を満たすように選びます。 (i×j) mod 2019 の最小値を求めてください。
これは難しくないですよ。 L から R までのすべての組み合わせを計算して余りを出せば良いです。すべての組み合わせを計算するというのは、前にやった B 問題で学んだことですね。
でも単純に総当たりするのは芸がありません。ある数を 2019 で割った余り m は 0 ≦ m ≦ 2018 ですから、0 が出たらそこで終了、答えは 0 です。0 が出なかったら答えは 2018 以下の最も小さな数になりますね。最も小さな数を抽出するのは A 問題でもやりました。
では、以下の流れでプログラムを書いてみましょう。
プログラム自体は簡単です。チャチャっと書いて入力例を試してみると、うまくいきました。
えらい簡単だったなぁ。でもまぁとりあえずいろんな数値入れて試してみよう。と、大きめの数も入れて確認していくと、500000 あたりからおかしな答えが出てきて、1000000 超えると負数になっちゃった (^_^;)
あれ〜、オーバーフローしちゃったのか〜
与える数の最大値が 2×109 だったので int 型でいけるよねぇ〜と考えていたのですが、余りの計算式を
remain = i * j % 2019
としたために、ここでオーバーフローしてしまったようです。計算の最大値は 4×1018 になりますから long long int 型にしなければいけません。
うーん、int 型でオーバーフローしないような計算方法はないのかなぁ〜 と、アルゴリズムを考え直してみるのは B 問題で学んだことです。
考えること小一時間 (^_^;) あった!合同式です。
合同式 a ≡ b ( mod n ) a を n で割った余りは b
合同式の掛け算は
a ≡ c ( mod n ) 、b ≡ d ( mod n ) のとき、a × b ≡ c × d ( mod n )
となります。つまり、「2つの数の積 a × b を n で割った余りは、a、bをそれぞれ n で割った余りの積 c × d と同じ」ということ。このことから前出の計算式は次のようにできます。
remain = ( i % 2019 ) * ( j % 2019 ) % 2019
これで最大値は 2×109 に抑えられますから int 型で計算できるようになりました。
なお、合同式については「合同式の証明や問題の解き方を解説!大学受験で使いこなそう!」を参考にさせていただきました。
ということで、できあがったプログラムです。
/* AtCoder Beginner Contest 133 C - Remainder Minimization 2019
2019.07.25 by meyon */
#include <stdio.h>
int main()
{
// input date
int L, R;
scanf("%d %d", &L, &R);
if (0>L || L>=R || 2000000000<R) {
printf("Error!¥n");
return 1;
}
// modulo
int ans=2018;
for (int i=L; i<R; i++) {
for (int j=i+1; j<=R; j++) {
int remain=(i%2019)*(j%2019)%2019;
if(ans>remain) {
ans=remain;
}
if (0==ans) {
i=R;
j=R+1; // end of modulo
}
}
}
// answer
printf("%d¥n", ans);return 0;
}
余りが 0 になったときに二重のループを一気に抜けて計算を終了させるのは、条件式が偽になるような値を代入する方法をとっています。break では一つのループしか抜けられません。
今回はなかなか良い感じだろうと自画自賛してますが、提出はやっぱりドキドキしますね (^_^;)
]]>
/* AtCoder Beginner Contest 133 B - Good Distance
2019.07.25 by meyon */
#include <stdio.h>
#include <stdlib.h>
void err()
{
printf("Error!¥n");
exit(1);
}
int main()
{
// input N:point and D:dimension
int N, D;
scanf("%d %d", &N, &D);
if(N<2 || N>10 || D<1 || D>10) {
err();
}
// input X:coordinates
int X[N][D];
for(int i=0; i<N; i++) {
for(int j=0; j<D; j++) {
scanf("%d", &X[i][j]);
if(X[i][j] < -20 || X[i][j] > 20) {
err();
}
}
}
// calculate norm
int ans=0;
for(int i=0; i<N-1; i++) {
for(int j=i+1; j<N; j++) {
int norm=0;
for(int k=0; k<D; k++) {
int diff = X[i][k] - X[j][k];
norm += diff * diff;
}
// check norm whether integer
int flag=0;
for(int k=0; k*k <= norm; k++) {
if(k*k == norm) {
flag=1;
}
}
if(flag) {
ans++;
}
}
}
// display answer
printf("%d¥n", ans);
return 0;
}
err() 関数は Error! を表示してプログラムをエラー終了します。exit() を利用するために stdlib.h をインクルードしています。
点の数 N と次元数 D を入力したら、その数で座標の配列変数を定義します。for 文で座標データを入力します。
calculate norm と check norm whether integer の部分は解説の実装例を参考にしました。ループの条件式がちょっと異なっているのと、差分の絶対値とはせずそのまま差をだしているところが相違点です。
ちなみに diff は差分、norm はベクトルの距離みたいな意味ですね。
実装例のように flag を boolean 型にするには別のヘッダファイルが必要になります。まぁ int 型でも問題ないかなと。
提出した結果、AC をいただきました。
今回はちょっと難しかったですけど、考え方さえ分かればなんとかなりそうですよね。たくさん勉強させていただきました。
]]>
流れとしては、
といった感じですが、俺にはどうすればよいのかわからない部分があります。
一つは「3. Yij を計算」について。次元の数 D によって計算する項目数が変わってきます。D によって式を変化させるにはどうすればよいのでしょうか。もう一つは「5. すべての点間で繰り返す」です。漠然と「for 文を使って N、D の数だけ繰り返す」というイメージを浮かべますが、具体的な方法に考えが進みません。
今の俺の実力は「考えて悩むよりも真似してみよう」レベル (^_^;) 幸い解説に C++ の実装例がありましたので、これを参考にしてみましょう。
実装例で計算を行なっている部分は以下です。なるほど!と思ったのは太字の部分、( ) を計算し二乗して加算代入していくというところです。これで二つの疑問点は一気に解決できそうです。
for (int i = 0; i < N; ++i) {
for (int j = i+1; j < N; ++j) {
int norm = 0;
for (int k = 0; k < D; ++k) {
int diff = abs(X[i][k] - X[j][k]);
norm += diff * diff;
}
では、入力例 2 の N=3、D=4 の場合で考えてみましょう。
座標は、
1 番目の点 (X00 X01 X02 X03)
2 番目の点 (X10 X11 X12 X13)
3 番目の点 (X20 X21 X22 X23)
で、配列変数 X[N][D] に格納されています。
最初に 1 番目の点と 2 番目の点の距離の二乗 Y01 を求めます。
Y01 =(X00-X10)2+(X01-x11)2+(X02-X12)2+(X03-X13)2
次に 1 番目の点と 3 番目の点の距離の二乗 Y02 を求めます。
Y02 =(X00-X20)2+(X01-x21)2+(X02-X22)2+(X03-X23)2
最後は 2 番目の点と 3番目の点の間です。
Y12 =(X10-X20)2+(X11-x21)2+(X12-X22)2+(X13-X23)2
これですべての点の間の計算は終わりです。
X[i][k]-X[j][k] とすると、
for (i=0; i<N-1; i++) {
for (j=i+1; j<N; j++) {
for (k=0; k<D; k++) {
X[i][k] - X[j][k]
といったループを作ればよいことがわかります。
ちなみに、N=10 とすると必要な計算式は、i=0 のときに 9 個、i=1 のときに 8 個、……、i=8 のときに 1 個の計 45 個 (10C2=45) となります。実装例では i=9 までループしますが、次の j のループが偽となって終了するので、結果は変わりません。ループが 1 回空振りするだけですね。
なお、実装例では差分の絶対値をとっていますが、どうせ二乗するのですから不必要だと考えています。
さぁて、ようやくアルゴリズムも理解できたので、次回こそプログラムを書いてみることにしましょう。
]]>
AtCoder Beginner Contest 133 の B 問題「Good Distance」です。
この問題はピンときました。座標上の 2点の間の距離を求めるのですから、いわゆる三平方の定理ってやつですね。
多次元の場合については俺は知らなかったのですが、問題に示された公式で適用できるのでしょう。基本的な考え方は三平方の定理も同じなんだろうなと想像できます。
例 1 を見てみましょう。
N=3 、D=2 ですから、平面 (二次元空間) 上に 3 個の点があるということです。ごく普通に三平方の定理で考えればいいですね。
たとえば 1 番目の点 (1,2) から 2 番目の点 (5,5) までの距離は √( (5-1)2 + (5-2)2 ) です。 え〜と式の表示が変ですけど、笑ってこらえてやってください (^_^;)
さて、次にこの式の値が整数かどうかを判定しないといけませんが、えーと、どうしましょう? 思いついたのは以下のようなものです。
でもなんだかねぇ、なぜだかいまいちすっきりしないんですよ…
迷ったのでちょっと解説をカンニングしてみました。すると
k2=Yij を満たす k が存在するかを調べれば √ Yij が整数であることを判定できます。
とあります。
そうか、平方根というムズムズするような値を判定するんじゃなくて、整数 k を二乗した値が (5-1)2+(5-2)2 と等しいかどうかを調べればよいのですね。それならすべての変数を int 型で扱えます。
ポン、なるほど、目から鱗のアルゴリズム (^_^;)
例 2 はどうでしょうか。
N=3 、D=4 ですから四次元空間上に 3 個の点が存在します。四次元空間…… う〜ん、四次元空間を想像することはできませんが、公式に当てはめて計算するだけのことです。
1 番目の点から 2 番目の点までの距離は、
Y12=(-3+12)2+(7-1)2+(10-8)2+(2-2)2=121
121 は 112 なので真 (整数) です。
2 番目と 3 番目の間は 、
Y23=(-2+12)2+(8-1)2+(10-9)2+(3-2)2=151
151 は 122 と 132 の間なので偽 (整数ではない) です。
同様に 1 番目と 3 番目の間は、
Y31=(-2+3)2+(8-7)2+(9-8)2+(3-2)2=4
これは 22 ですから真です。
したがって真が 2 つでしたから、出力は 2 となります。
例 3 は直線 (一次元空間) 上に 5 個の点があります。座標はすべて整数ですから、これらの点の間の距離は、計算するまでもなく整数であるとわかります。でも、プログラムでは何次元でも同じように処理できなければなりませんので、公式に当てはめてみましょう。
1 番目と 2 番目の間は Y12=(2-1)2=12 で真です。2 番目と 4 番目の間も Y24=(4-2)2=22 で真です。3 番目と 5 番目の間も Y35=(5-3)2=22 で真です。他の組み合わせもすべて同じく真になります。組み合わせの数は 10 ( 5C2 ) です。
さて、これで問題が理解できましたので、次回はプログラムを考えてみることにします。
]]>
コンテストをよく見てみると「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 となります。
わかりましたよ。こうしましょう。
で、こんなふうになりました。
/* 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^)/
]]>
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 ≦ 105 、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 スケッチは 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 の出力を未接続にしています。このあたりはプログラムと整合させないとうまく表示できませんので注意します。
スケッチのフローチャートです。
スイッチの ON を検出して 0 から 99 までカウントします。それを 7 セグメント LED で 2 桁表示させます。十の位が 0 のときは表示しないようにしています。2 桁を 1 ミリ秒ずつ表示して 300 回繰り返すので、約 600 ミリ秒ごとにカウントしていくことになります。
その値の倍数かどうかは剰余が 0 かどうかを判断します。3 と 5 の倍数ということは 15 の倍数ということです。ここが 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 程度の電解コンデンサを置くようにしています。
電源が写真にはありませんが、12V の AC アダプタの出力から 3 端子レギュレータで 5V を作っています。Arduino の電源は 12V を Vin に供給しています。ただ、USB ケーブルを挿したまま AC アダプタを切っちゃうと Vin から 5V が逆流してしまうので、12V ラインにダイオードを入れて阻止しています。まぁ小ネタですけど。
]]>
廉価な互換機ということなので、Uno との違いもさることながら、正規品との違いも確認しておきたいとググってみたのですが、どうもなにやら良い印象がない (^_^;)
レビューでも数個に 1個は動作しないとか、USB シリアル変換チップ CH340 用のドライバをインストールしないといけないとか、その CH340 が偽物であるとか、Old Bootloader を選択しないと書き込みできないとか… 使ってみたというサイトを見ても同様の情報が多く、また中国からの配送なので時間がかかるとか、まぁ導入時は多少苦労するかなと覚悟をしていました。
が、そんな懸念は一切無用。Ubuntu 18.04.2 LTS のパソコンで、次の設定で動作しています。
ボード:Arduino Nano
プロセッサ:ATmega328P
シリアルポート:/dev/ttyUSB0
CH340 のドライバのインストールは不要です。ブートローダも Old ではないほうで良いので、新しいものになっているようです。ちなみに CH340 の真贋の判断基準となるとか言われているマークは底の平らな大きな丸でした。見た目も気になるようなところは何もなく、粗悪な安物といった感じはありません。
USB ケーブル (A to miniB) とその他 1点とともに、注文から約 24時間で到着しました。さすが Amazon's Choice です。寸法はわかっていましたが、実物は思っていた以上に小さく感じました。A コネクタと変わらないですね。
まず、3個すべてに Blink を書き込んで正常に動作することを確認しました。
付属のピンヘッダを取り付けて、以前作った 3x3x3 LED Cube につないでみました。これも全く問題なく動作しています。このサイズの基板で実装できそうですね。
]]>
タクトスイッチ 6個を「up」「down」「right」「left」「set」「reset」のファンクションボタンとし、どのボタンが押されたかを検出します。今回の肝は電圧分圧方式でのボタン検出で、押したボタンによって入力電圧が変化するという仕組み。ググると考え方や抵抗値の計算方法が出てきますので参考にしてください。
二つ以上のボタンを同時に押した場合は優先順位がありますので、今回は「reset」を最優先のボタンとして並べてみました。
計算上の分電圧と実際の入力値は異なりますので、Arduino の入力値を実測してボタン判断のしきい値を決めています。
どのボタンが押されたかを表示するために、手元にあった 7 セグメント LED (アノードコモン) をシフトレジスタで表示させています。シフトレジスタは前に LED キューブで使っていた SN74LS164 です。こいつはストレージレジスタがありませんので、ドライブ用にトランジスタ 2N3906 を付けました。これによりデータ送信中は LED を表示させないようにしています。セグメントの電流は 7mA 程度、2N3906 は最大コレクタ電流 200mA ですので十分使えます。
回路図です。
ブレッドボードはこんな感じ。実際はこれを時計方向に 90° 回して実験していますので、右上のタクトスイッチは上下左右に 4 個のボタンと左下に reset 、右下に set という配置になってます。まぁどんな並びでも関係ないんですけど (^_^;) 押したボタンに応じて数字が表示されるという単純なものです。
フローチャートとスケッチです。ボタン判断のしきい値がフローチャートとスケッチで異なっていますが、最終的にはスケッチの値にしています。
// 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 で点灯になります。これ勘違いするとすべてがおかしくなってしまいますので (^_^;)
]]>
アクションは「シングル (一回押し) 」「ダブル (二回押し) 」「ホールド (長押し) 」の三種類で、これらを一つのタクトスイッチから検出し 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 を使う方法では、プログラムがここで止まってしまうのでうまくありません。
ということでできたスケッチは以下です。
// 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 を入れると、その場所で変数がどうなっているかが見えるようになります。うまく動かないときに重宝しますね。
ということで、スイッチアクションで制御を切り替えることができるようになりました。
]]>
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
以前は 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 機を最後に更新したのは 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 を完了しました。
]]>
However, some drives will not immediately remap such sectors when written; instead the drive will first attempt to write to the problem sector and if the write operation is successful then the sector will be marked good (in this case, the "Reallocation Event Count" (0xC4) will not be increased). This is a serious shortcoming, for if such a drive contains marginal sectors that consistently fail only after some time has passed following a successful write operation, then the drive will never remap these problem sectors.
代替保留中の不良セクタは、もう一度読み出しに失敗すると代替処理待ちとなり、次の書き込みで代替処理が行われます。しかし、「一部のドライブでは書き込みを行なってもただちに代替処理が行われない」というのです。不良セクタに対して、まず書き込みを行なってみて、書き込みが成功すれば良いセクタだとマークされます。この場合は Reallocation Event Count (0xC4) を増やしません。
まさにこれではないのでしょうか。
くだんの HDD は、dd コマンドでゼロフィルしたのちに Current Pending Sector が 0 になった。でも、Reallocated Event Count も Reallocated Sector Ct も増えていない。つまり、書き込みを行なったにもかかわらず代替処理が行われなかった。それは、代替処理の前に書き込みを行ない、それが成功したためだと。
これは、場合によっては不良セクタの代替処理が行われないことになるので「重大な欠点だ」と書かれています。
微妙な位置に立たされた HDD ということでしょうねぇ (^_^;)
ちなみに「198 0xC6 (Offline) Uncorrectable Sctor Count」について。
The total count of uncorrectable errors when reading/writing a sector. A rise in the value of this attribute indicates defects of the disk surface and/or problems in the mechanical subsystem.
この数値が増えるということは、「ディスク表面の欠陥」「メカニカルサブシステムの問題」があるということ。これはどうやら今後急速に劣化していく可能性を持っているようです。
ということで、最終結果。
この HDD は近いうちに故障する恐れがあります。でも、貧乏な俺はこの HDD を、あまり重要でない用途に、常に状態をチェックしながら使い続けてみることにします。\(^o^)/
]]>
で、完全に動かなくなるまでハードディスクを使い倒したい貧乏な俺は、いろいろ試しながら、グーグル先生に教えてもらいながら、あわよくばこいつを再利用できないものかと目論んでいたわけです (^_^;)
多くのサイトで勉強させていただきましたが、そんななかで出会ったサイトがこちら「PCと解 パソコントラブルにまつわるサイト」です。さまざまなパソコントラブルについてこれまでのご経験をもとに書かれているサイトですが、ハードディスクトラブルに関してもわかりやすく書かれています。とても参考になりました、ありがとうございます。
さて、おれが正確に理解できているかかどうかは怪しげです (^_^;) が、つまりこういうことかと。
S.M.A.R.T. テストは HDD の物理的な状態を調べるもの。
Windows で使ってみたらエラーもなく利用できたのは、ファイルシステムが物理的な異常を論理的に処理、回避してくれているから。Windows で動くチェックディスク chkdsk で不良セクタが表示されないのはそのためだということ。
S.M.A.R.T. によるセルフテストは読み書きのチェックを行ないますが、最初に不良セクタを見つけたところで停止します。badblocks コマンドも同じように読み書きを行ないますが、エラーがあっても停止せず HDD 全体をチェックしてくれます。
不良セクタをファイルシステムに論理的に認識させるには、Windows ならば chkdsk 、Linux ならば fsck のようなファイルシステムをチェックするコマンドを利用します。でも、すでに代替処理されたセクターにファイルシステムは関知しませんから、OS からは見えません。
読み出しエラーがあったセクターは Current Pending Sector となり登録されます。次に読み出しがあったときに、読み出しに成功すれば正常なセクタとなり Current Pending Sector の数は減ります。読み出しに失敗すると代替処理すべきセクタとなり、次の書き込み時に代替処理されます。
代替処理が行われると Reallocated Event Count が増やされます。処理済みとなれば Reallocated Sector Ct が増える。でも、処理に失敗した場合はどうなるのでしょうか? その場合は Offline Uncorrectable となる?
Current Pending Sector に書き込みを行なう ( dd や badblocks ) と代替処理が行われるので、Current Pending Sector は消失します。chkdsk や fsck はファイルシステムのエラーをチェックするものなので、いわゆる修復 (代替処理) にはならない。フォーマットもファイルシステム上の処理なのでダメ。
これらを踏まえてくだんの HDD のことを考えてみると。
dd コマンドでゼロフィルしたのちに、S.M.A.R.T. のセルフテスト (long) を実行し Current Pending Sector は 0 になった。でも、Reallocated Event Count も Reallocated Sector Ct も増えていない。Offline Uncorrectable は 108 が 209 に増えた。
とゆーことは、書き込みを行なったにもかかわらず代替処理が行われなかった。代替保留中だけれども、次の読み出しがまだ行われておらず、代替処理すべきセクターになっていなかった、ってことか? そこへ書き込み、読み出しを行なったら正常にできたので、代替保留中にはならなかった?
…… じゃあ、Offline Uncorrectable はなんなの?
まとめようかと思ったけれど、疑問はさらに続く…… (^_^;)
]]>
badblocks コマンドを使って調べてみることにしましょう。
badblocks は、今回は破壊的 (書き込みモード) 検査をやってみます。man badblocks してみると、
-w Use write-mode test. With this option, badblocks scans for bad blocks by writing some patterns (0xaa, 0x55, 0xff, 0x00) on every block (すべてのブロックにいくつかのパターンを書き込むことによって) of the device, reading every block and comparing the contents. (後略)
ということです。ん〜、4つのパターンをすべてやってみるのかな。1パターンに 5時間かかるとすると 20時間 … -t オプションでパターンを random に指定すれば良いのかもしれない。
-t test_pattern
Specify a test pattern to be read (and written) to disk blocks. The test_pattern may either be a numeric value between 0 and ULONG_MAX-1 inclusive, or the word "random", which specifies that the block should be filled with a random bit pattern ("random" は、ブロックがランダムなビットパターンで埋められることを指定します) . (中略) If multiple patterns are specified then all blocks will be tested with one pattern before proceeding to the next pattern (複数のパターンが指定されている場合は、すべてのブロックが1つのパターンでテストされた後に、次のパターンに進みます。) .
ということなので、以下のように実行してみることにします。
$ sudo badblocks -svw -b 4096 -c 256 -t random -o badblocks.txt /dev/sdb
ランダムなパターンを書き読みして、一回りで終了。1 ブロック 4096 バイトを 256個ずつテストしますので、一度に 1MB をテストすることになります。と思います (^_^;)
ちなみに、エラーを書き出したファイルを e2fsck に渡すといった方法を説明しているサイトがいくつも見られましたけど、いま俺がやっているような、全体を潰したファイルシステムが無い状況ではそれはできません。e2fsck はファイルシステム (ext2 / ext3) がないと実行できませんので。俺がファイルに書き出しているのは、単に記録として見るためです。
man には、e2fsck に渡す場合には badblocks を使わないように、と書かれています。
Important note: If the output of badblocks is going to be fed to the e2fsck or mke2fs programs (badblockの出力がe2fsckまたはmke2fsプログラムに送られる予定の場合) , (中略) it is strongly recommended that users not run badblocks directly, but rather use the -c option of the e2fsck and mke2fs programs (ユーザーが badblocks を直接実行しないことを強くお勧めします。むしろ、e2fsckおよびmke2fsプログラムの-cオプションを使用してください) .
さて、実際の動きですが、開始からまずランダムなパターンの書き込みが行われたようです。4時間 40分で 100% に達し、
Testing with random pattern: done
となりました。現在のところエラーは検出されていません。
そして今度は Reading and comparing: が進行しています。たしかに、dd で全体に 0 を書き込むにも同じ時間がかかっていますから、納得できる作業時間です。
ということはですよ、デフォルトが 4つのパターンを指定しているとすると、書き込みだけで 18時間 40分を要することになります。読み出しと比較に同じ時間かかるならば、全体の作業時間は 37時間 20分と見積もられます。
そしてついに badblocks コマンドが終了しました。正確な時間をチェックしてないのですが、ほぼ書き込みと同じほどかかっています。
$ sudo badblocks -svw -b 4096 -c 256 -t random -o badblocks.txt /dev/sdb
Checking for bad blocks in read-write mode
From block 0 to 122096645
Testing with random pattern: done
Reading and comparing: done
Pass completed, 0 bad blocks found. (0/0/0 errors)
結果、不良セクタは検出されませんでした。エラーがなかったので badblocks.txt も作成されていません。そして予定したとおり、ランダムパターンの書き込み読み出しを一回り実行して終了ということでした。
どうなんでしょう? この HDD ってまだしばらく利用できそうじゃないですか (^_^;)
]]>
全ブロックに 0 を書き込んでみたいのですが、方法として、いつも HDD の廃棄のときに利用している shred コマンドで可能です。でも今回は、あまり使ったことがない dd コマンドでやってみようと思います。
なお、時間が長くなるので、以下、別のパソコン (linuxBean) で作業を行いました。
$ sudo dd if=/dev/zero of=/dev/sdb bs=4096
多分 4時間ぐらいはかかるのでしょうけど、進行状況がわかりません。そこで、別のターミナルを開いて
$ sudo killall -USR1 dd
とすると進行状況が表示されます。定期的に表示させるには watch コマンドを使うとよいです。
$ sudo watch -n 600 killall -USR1 dd
-n の値が繰り返しの秒数です。頻繁に表示しても仕方がないので、10分おきにしています。
ちなみに、ubuntu 18.04 の man dd で調べると、
status=LEVEL
The LEVEL of information to print to stderr; (中略) 'progress' shows periodic transfer statistics ( 'progress' は定期的な転送統計を表示します)
と書いてあるので、status=progress をつければ良さそうですね。試してませんが (^_^;)
ubuntu の dd のバージョンは 8.28 ですが、linuxBean は 8.13 でこのオプションはありませんでした。
で、結果 dd は 4時間 40分で終了しました。続けて S.M.A.R.T. テストを行います。
$ sudo smartctl --test=long /dev/sdb
予定時間は 3時間 50分ほどですので、パソコンにおまかせして就寝します (^_^;)
一夜明けて、テストの結果を確認しました。
$ sudo smartctl -a /dev/sdb
テスト評価は PASSED です。
SMART overall-health self-assessment test result: PASSED
ログのステータスも正常に終了しています。
Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error
# 1 Extended offline Completed without error 00% 25478 -
属性データの抜粋です。
ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE
5 Reallocated_Sector_Ct 0x0033 100 100 024 Pre-fail Always - 1 (1999, 1)
196 Reallocated_Event_Count 0x0032 100 100 000 Old_age Always - 1 (5, 13648)
197 Current_Pending_Sector 0x0012 100 001 000 Old_age Always - 0
198 Offline_Uncorrectable 0x0010 001 001 000 Old_age Offline - 209
属性データについてざっとググってみたけど、なるほどって思えること書いてあるサイトにまだ出会っていません。今の俺には難しすぎる解説ばかりってことです (^_^;)
197 Current_Pending_Sector (現在保留中のセクタ) はなくなりました。予備軍は一掃されたってことですかね。
5 Reallocated_Sector_Ct (再割当てされたセクターの数) は 1 のままです。代替領域に再割当てされた不良セクターですので、OS からは見えない奴です。(1999, 1) というのは代替領域が 2000 あるってことでしょうか?
196 Reallocated_Event_Count (再割当てイベントの回数) は再割当てを試行した回数です。
198 Offline_Uncorrectable (オフライン修正不可能) が当初の 108 から 209 に増えました。再割当てできなかったセクターらしく、こいつがいわゆる不良セクターって奴かな?
それでは次は、badblocks コマンドで不良セクターの検査をしてみましょう。
]]>
以下、壊れてもかまわない、データも消失してかまわないというレベルでの話ですので、真似してみようという物好きな人も自己責任でお願いします。
セルフテストの評価は PASSED になっています。
SMART overall-health self-assessment test result: PASSED
セルフテストのログです。read failure で中断しています。
Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error
# 1 Short offline Completed: read failure 90% 25453 172907425
属性データの抜粋です。
ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE
5 Reallocated_Sector_Ct 0x0033 100 100 024 Pre-fail Always - 1 (1999 1)
196 Reallocated_Event_Count 0x0032 100 100 000 Old_age Always - 1 (5 13648)
197 Current_Pending_Sector 0x0012 058 058 000 Old_age Always - 43
198 Offline_Uncorrectable 0x0010 046 046 000 Old_age Offline - 108
不良セクタに関連する数値の抜粋ですが、けっこう悪くなっている感じです。
テストでエラーになる LBA は 172907425 です。これがあるパーティションは /dev/sdf2 だということが以下でわかります。
$ sudo fdisk -l /dev/sdf
ディスク /dev/sdf: 465.8 GiB, 500107862016 バイト, 976773168 セクタ
単位: セクタ (1 * 512 = 512 バイト)
セクタサイズ (論理 / 物理): 512 バイト / 512 バイト
I/O サイズ (最小 / 推奨): 512 バイト / 512 バイト
ディスクラベルのタイプ: dos
ディスク識別子: 0x3f3c3b21デバイス 起動 開始位置 最後から セクタ サイズ Id タイプ
/dev/sdf1 * 2048 1126399 1124352 549M 7 HPFS/NTFS/exFAT
/dev/sdf2 1126400 976771071 975644672 465.2G 7 HPFS/NTFS/exFAT
ファイルシステムは NTFS でブロックサイズは 4096 バイト、1 セクターは 512 バイトですから、問題の LBA が含まれるブロックは、
(172907425 − 1126400 ) × 512 ÷ 4096 = 21472628
となります。
ちなみに、ブロックサイズは NTFS では Windows のコマンドプロンプトで
> fsutil fsinfo ntfsinfo X:
でわかります。ext3 などは Linux で
$ sudo tune2fs -l /dev/sdf2
で調べることができます。
このブロックを潰します。
$ sudo dd if=/dev/zero of=/dev/sdf2 bs=4096 count=1 seek=21472628
1+0 レコード入力
1+0 レコード出力
4096 bytes (4.1 kB, 4.0 KiB) copied, 0.000202855 s, 20.2 MB/s$ sudo sync
sync はメモリーとディスクを同期させるっていうコマンドです。
S.M.A.R.T. ショートテストしてみましょう。
$ sudo smartctl --test=short /dev/sdf
結果のログです。Completed without error となりました。
Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error# 1 Short offline Completed without error 00% 25457 -
# 2 Short offline Completed: read failure 90% 25453 172907425
属性データもみてみましょう。
ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE5 Reallocated_Sector_Ct 0x0033 100 100 024 Pre-fail Always - 1 (1999 1)
196 Reallocated_Event_Count 0x0032 100 100 000 Old_age Always - 1 (5 13648)
197 Current_Pending_Sector 0x0012 059 058 000 Old_age Always - 42
198 Offline_Uncorrectable 0x0010 046 046 000 Old_age Offline - 108
ん〜、Current_Pending_Sector が一つ減っただけです。一つ潰しただけだから当然ですよねぇ。潰したけど Reallocated_Sector_Ct が増えないのはなぜ? HDD の詳しい知識がないので俺にはわかりません m(__)m
で、次にロングテスト ( 4時間近くかかりました) を行うと別の LBA でエラーが出ます。それを潰して、またロングテストして… あ〜それを 40回以上やります? つまり、Current_Pending_Sector が一つぐらいならこうして潰してもいけるのかな、って感じ。こんなに傷んでる HDD ではやる価値ないってことです。
じゃぁ次は、あっさり全部を 0 で潰してしまいましょうか。
]]>
回路図です。
LED はアノードコモンで、アノード側のトランジスタ 2SA1015 でレイヤを制御します。レイヤのインターロックは省略しました。まぁ、気分です (^_^;)
シフトレジスタは 2段に繋いで、16コラムを制御しています。
スケッチです。
#include <MsTimer2.h>
int ptn[][4] = { // Pattern data
//{LayerA,LayerB,LayerC,LayerD},{0x0001,0x0000,0x0000,0x0000},
{0x0013,0x0000,0x0000,0x0000},
{0x0137,0x0000,0x0000,0x0000},
{0x137f,0x0000,0x0000,0x0000},
{0x37fe,0x0001,0x0000,0x0000},
{0x7fec,0x0013,0x0000,0x0000},
{0xfec8,0x0137,0x0000,0x0000},
{0xec80,0x137f,0x0000,0x0000},
{0xc800,0x37fe,0x0001,0x0000},
{0x8000,0x7fec,0x0013,0x0000},
{0x0000,0xfec8,0x0137,0x0000},
{0x0000,0xec80,0x137f,0x0000},
{0x0000,0xc800,0x37fe,0x0001},
{0x0000,0x8000,0x7fec,0x0013},
{0x0000,0x0000,0xfec8,0x0137},
{0x0000,0x0000,0xec80,0x137f},
{0x0000,0x0000,0xc800,0x37fe},
{0x0000,0x0000,0x8000,0x7fec},
{0x0000,0x0000,0x0000,0xfec8},
{0x0000,0x0000,0x0000,0xec80},
{0x0000,0x0000,0x0000,0xc800},
{0x0000,0x0000,0x0000,0x8000},
{0x0000,0x0000,0x0000,0x0000},};
unsigned long cycle =200; // Pattern cycle
int dataPin = 2;
int clockPin = 4;
int layer[4] = {8,9,10,11};
int nptn = sizeof(ptn) / sizeof(ptn[0]);
int cptn = 0;
void chptn(){
cptn++;
if (cptn > nptn-1)
cptn = 0;
}
void setup(){
pinMode(dataPin, OUTPUT);
pinMode(clockPin, OUTPUT);for (int i=0; i<4; i++){
pinMode(layer[i], OUTPUT);
digitalWrite(layer[i], HIGH);
}shiftOut(dataPin, clockPin, MSBFIRST, 0xff00 >> 8);
shiftOut(dataPin, clockPin, MSBFIRST, 0xff);
MsTimer2::set(cycle, chptn);
MsTimer2::start();
}
void loop(){
for (int i=0; i<4; i++){
shiftOut(dataPin, clockPin, MSBFIRST, ~ptn[cptn][i] >> 8);
shiftOut(dataPin, clockPin, MSBFIRST, ~ptn[cptn][i]);
digitalWrite(layer[i], LOW);
delay(1);
digitalWrite(layer[i], HIGH);
}
}
パターンデータは 16進数になっています。
起動時にレイヤとコラムをすべて HIGH に初期化し消灯させています。
パターンの切り替えは MsTimer2 で割り込み処理しています。
シフトレジスタへの出力は shiftOut 関数を使います。LOW で点灯なのでパターンデータを NOT しています。シフト出力中に LED が点灯しないように、出力が終わってからレイヤを ON します。
その他、基本的に 3x3x3 と同様ですね。
ブレッドボードです。
今回は 0.5mm 単線で配線しました。ストリッパー使ったり面倒っちゃ面倒ですけど、まぁ慣れです。
]]>
ググると SN74HC595N がよく出てくるのですが、いつも行くパーツ屋さんには在庫がなかった。毎度のことだけど品揃えは悪い。何か代わりはないかなと探して見つけたのが SN74LS164N という TTL のシフトレジスターです。CMOS のもあったのですけど、こっちが安かったから (^_^;) SN74HC595N と比較するとこちらはストレージレジスターがありませんが、まぁ実験に使うには問題ないでしょう。
さてとりあえず、あまり考えもせずに下のような回路を組んで動かしてみました。
スイッチを押すと LED が順番に点灯し、離すと順番に消えていくという回路です。の、はずです。
が、スイッチを押すと 8 個の LED がいっぺんに点灯する… 離すといっぺんに消灯する… しかもクロックの立下りでも反応する… なんでやねん (^_^;) ???
さんざん悩んで、ググって、でも SN74LS164N の情報がほとんどない。LED の駆動にバッファを入れたり、スイッチにコンデンサー入れたりしてみたけど、まったく改善しない。
そしてようやく見つけたサイトがここ 8-bit shift register 74LS164 not working でした。まさに今の俺と同じ症状です。
原因はクロックの波形が綺麗でないのではないかということ。対策は波形整形のためにシュミット回路を入れてみる、でした。早速シュミットインバータを挿入してみると、みごとに LED が順番に点灯してくれました。
そしてもう一つ、この記事に書かれていたのは、SN74LS164N から取り出せる電流の推奨値は 0.4mA だということ。逆に出力へ流し込むように使うほうが良いのだということでした。たしかにデータシートの推奨値を見ると IOH = -0.4mA 、IOL = 8mA となっています。
一般的に TTL の場合、入力は LOW で吐き出し、出力は LOW で吸い込みですから、その通りだと納得です。基本を忘れてはいけません (^_^;) 逆に入力が HIGH では電流がほとんど流れませんから、クリア (CLR) 入力のように直接 +5V ラインにつなげます。
ということで、最終的に出来上がったシフトレジスターのテスト回路は以下のようになりました。
出力 LOW で点灯ですので、データ入力も LOW で点灯、HIGH で消灯となります。クリア入力は常に HIGH にしていますが、これを LOW にすると出力がクリアされます。ただし、クリア時は出力 LOW となり全点灯します。
CMOS で HIGH 時点灯に慣れているとちょっと使いにくいかもしれませんね。
]]>
Cube の組み立てはググるといろいろ出てきますので参考に。今回は LED のリードを折り曲げ加工してはんだ付けで組み立てましたが、数が多くなるとかなり難しいですね。治具に固定して作業するのですが、逆にフレームを作って LED を取り付けていくといった方法を考えたほうが良さそうな気がします。
ケースに収めて仕上げるのも良いのですが、今回はここまでにしようと思います。
回路は、Arduino で直接駆動できる電流値を確認しているのでバッファをなくしました。その代りスケッチをミスってもレイヤが同時に ON しないようにインターロックを残します。それらを 5cm x 7cm のユニバーサル基板に組んで、ジャンパーで Arduino に繋ぐようにしました。
最終的な回路は以下のようになっています。スケッチは「LED Cube 3x3x3 を作ってみた - Arduino スケッチ」の通りです。
さてと、次は Cube を 4x4x4 にして、シフトレジスターを使ってみようと考えています。
]]>
しかぁし、NAS Navigator2 というアプリケーションは Windows 用で ubuntu では使えない。ubuntu をメインに使っているのに、NAS を起動させるために Windows を起動しなければならないとゆー本末転倒なことになっております (^_^;)
そこで、この PC連動電源機能を ubuntu でも利用できないか、という話。
Windows では何をしているのか。
WireShark でパケットのやり取りを見てみると、ときどき WOL (Wake on LAN) の Magic Packet が NAS へ送られていることがわかります。150 秒おきですが、なぜか 140 秒だったり 15 秒だったり…
「NAS Power Management Service」というサービスがこれを実行しているようで、Magic Packet は 3 回連続でポート 9 へ送られます。これを停止すると NAS は 300 秒後にシャットダウンします。NAS Power Management Service を再度起動すると Magic Packet がポート 9 へ送られ、NAS は起動します。
ちなみに NAS の WOL について説明書には、ポート 2304 を待ち受けていると書かれています。実際にはポート 9 でも起動できますけどね。
つまり、ubuntu でも 150 秒間隔で Magic Paket を送り続ければいいんじゃないのん?
スクリプト naspoweron.sh を書いてみました。
#! /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
これを ubuntu 機で実行すると NAS が起動、150 秒おきに Magic Paket が送信されて NAS は稼働し続けてくれました。
ではこのスクリプトを ubuntu server に仕込んじゃいましょう。そうすればサーバーが起動すれば NAS も起動するようにできます。スクリプトを自動起動させる方法はいくつかあるようですが、サーバーの自動シャットダウン用に crontab を使っているので、これを利用することにします。
$ sudo crontab -e
次の一行を追加します。
@reboot /home/hoge/naspoweron.sh
パケットの送出を確認するには tcpdump を使います。
$ sudo tcpdump host 192.168.1.xx
クライアントの ubuntu desktop 18.04 では「自動起動するアプリケーションの設定」に追加すれば良いですね。
ではしばらく使ってみて、どうなるかみてみよう (トランプ氏風に (^_^;) )
]]>
// Pattern data
int ptn[][3] = {
//{LayerA,LayerB,LayerC},
{0525,0252,0525},
{0252,0525,0252},
{0525,0252,0525},{0777,0000,0777},
{0070,0777,0070},
{0707,0707,0707},{0272,0272,0272},
{0555,0555,0555}
};
int colpin[9] = {2,3,4,5,6,7,8,12,13}; // Column output pin
int laypin[3] = {9,10,11}; // Layer output pin
void setup() {
for (int i=0; i<9; i++){
pinMode (colpin[i], OUTPUT);
}
for (int i=0; i<3; i++){
pinMode (laypin[i], OUTPUT);
}
}
void loop() {
unsigned long pcycle = 600; // Pattern cycle (ms)
int nptn = sizeof(ptn) / sizeof(ptn[0]);
for (int k=0; k < nptn; k++){
unsigned long ctime = millis();
while (millis() - ctime < pcycle){
for (int j=0; j<3; j++){
digitalWrite (laypin[j], HIGH);
for(int i=0; i<10; i++){
digitalWrite (colpin[i], ptn[k][j]>>i&1);
}
for(int i=0; i<10; i++){
digitalWrite (colpin[i], LOW);
}
digitalWrite (laypin[j], LOW);
}
}
}
}
点灯パターンを ptn[][3] に定義しています。
LED9 | LED6 | LED3 |
LED8 | LED5 | LED2 |
LED7 | LED4 | LED1 |
例えば「0525」では、最初の「0」が 8 進数であることを示し、次の 3 桁が点灯パターンを表しています。初めの「5」は LED9と LED7 を、次の「2」は LED5 を、最後の「5」は LED3 と LED1 を点灯させることを意味しています。これは 2 進数の「101010101」ですので、「0b101010101」と書いてもかまいません。ただし「B101010101」という書き方では 8 ビットしか扱えないようなのでコンパイルエラーになります。
同様に 10 進数でもよいので、「341」であれば LED9、7、5、3、1 が点灯します。ちなみに「0341」は 8 進数なので LED8、7、6、1 が点灯することになります。まぁ 10 進数では直感的にわからないですね (^_^;) 俺は 8 進数表記がわかりやすいです。
ひとつの { } 内の最初の数値が Layer A 、次が Layer B、最後が Layer C に対応するので、{ } が一つの点灯パターンになります。パターンは何行でも、変数メモリがある限り増やせます。
この ptn 配列変数をもとにして
for(int i=0; i<10; i++){
digitalWrite (colpin[i], ptn[k][j]>>i&1);
}
でコラムの 9 列の LED の点灯を制御します。変数 j でレイヤを切り替えて、一つの点灯パターンを完成させています。
そして変数 k の数だけパターンを変化させていきます。ptn の行数は定義していないので、
int nptn = sizeof(ptn) / sizeof(ptn[0]);
で行数を算出します。
一つのパターンの表示時間は pcycle で定義してあり、タイマーが pcycle を超えると次のパターンへ移行します。
unsigned long ctime = millis();
while (millis() - ctime < pcycle){︙
}
って感じです。
]]>
ざっとググってみると、LED でキューブを作ったという記事がたくさん出てきますから参考になります。でも、回路とかスケッチとかはやっぱり自分でやってみないとよくわからないなぁって印象でした。まぁ自分なりに考えてみましょう。LED のダイナミック点灯を試してみたいと考えていたので、ちょうど良いタイミングですし。
まず回路図を書いてみます。
LED は 9 個をカソードコモンに接続して 1 レイヤとします。それを 3 段重ねてダイナミック点灯させる。つまり各レイヤは同時に同じパターンで HIGH にし、カソード側を順次 ON にするという方法ですね。
9 本のコラム (アノード) と 3 本のレイヤ (カソード) は Arduino のデジタルピン 2〜13 で制御します。1 レイヤ当たりの LED 駆動電流は 60mA 弱ですので Arduino で直接駆動できますが、毎度のように駆動用にバッファを入れることにします。次の段階でこの部分をシフトレジスタに変更してみたいなという考えもあります。
パーツは手持ちのものを使っています。特別なものはまったくありません。
LED 駆動電流は 6.4mA 、レイヤ当たり 57.6mA です。レイヤ制御用トランジスタ (KSC1815Y) のコレクタ電流は 150mA なので十分ですね。ベース側のダイオードはレイヤが同時に ON しないようにするインターロックですが、プログラムで制御しますからやめてかまわないと思います。
バッファ (74VHCT244) は全体で 70mA ほどまで出力できますので、こちらも十分です。バッファの入力は Arduino の電源が落ちた時に不安定になるのでプルダウンしています。
レイヤ制御を 9~11 ピンにしているのはここを PWM にしたら明るさを変化させられるかなという狙いです。
これをブレッドボードで組み立てたのが下の写真です。
次はこれを制御するスケッチを書いてみましょう。
]]>
まぁ簡単なトラブルシューティングですが、ご参考までに。
なんちゅーかねぇ、窓際でのんびり過ごしたいのに、こんなんで呼び出されるわけですよ。ルーター再起動するぐらい営業さんでもできるでしょ、ってね。
]]>
過去に設定したときの記事 Ubuntu Server / ファイルサーバーを構築する と特に違いはありません。
1. samba をインストール
$ sudo apt install samba
今回インストールされた samba のバージョンは 4.7.6 でした。
2. ユーザーディレクトリのパーミッション設定
自分だけが読み書きできるようにパーミッションを変更します。
$ sudo chmod 700 /home/username
3. samba の設定
オリジナルの smb.conf を smborg.conf としてコピーしておきます。
$ sudo cp /etc/samba/smb.conf /etc/samba/smborg.conf
smb.conf を編集します。
$ sudo vi /etc/samba/smb.conf
ワークグループの変更
workgroup = WORKGROUP
共有ディレクトリの設定
[username]
path = /home/usernamebrowseable = yes
writeable = yes
read only = no
4. samba ユーザーを追加
$ sudo smbpasswd -a username
5. samba を再起動
$ sudo service smbd restart
Windows パソコンからファイルサーバーへアクセスできたら完了です。
]]>
基本的には過去記事 Ubuntu / ネットワークプレースを自動マウント でいけますが、このままでは NAS をマウントできませんでしたので一部修正しました。
ファイルサーバー (NAS) : 192.168.1.10
共有ディレクトリ : userdir
マウントポイント : /mnt/nas
ユーザー名 : nasuser
パスワード : naspasswd
まず cifs-utils をインストールします。
$ sudo apt install cifs-utils
つぎにマウントポイントを作成します。マウントポイントは、マウントする共有ディレクトリを格納する場所です。
$ sudo mkdir /mnt/nas
資格情報ファイル passwd.user を作成します。ファイル名は任意です。
$ sudo vi /etc/passwd.user
資格情報ファイルにユーザー名とパスワードを記述します。
username=nasuser
password=naspasswd
ファイルができたらオーナーとパーミッションを変更し、root 以外が読み書きできないようにします。
$ sudo chown root:root /etc/passwd.user
$ sudo chmod 600 /etc/passwd.user
/etc/fstab を編集します。
$ sudo vi /etc/fstab
次の一行を追加します。
//192.168.1.10/userdir /mnt/nas cifs vers=1.0,credentials=/etc/passwd.user,uid=1000,gid=1000,rw,defaults 0 0
我が家の NAS をマウントするためには vers=1.0 が必要でした。NAS の cifs バージョンが古いためだそうです。
rw は読み書きモード、defaults はマウントオプション、次の 0 は dump フラグ、最後の 0 は fsck チェックをしない、ということです。
サーバー起動時にマウントしますが、NAS が起動していないなどでマウントできなかった場合は
$ sudo mount -a
でマウントできます。
アンマウントしたいときは
$ sudo umount /mnt/nas
です。
]]>
ubuntu 64bit なので、fritzing-0.9.3b.linux.AMD64.tar.bz2 を Fritzing Download からダウンロードします。展開してできたディレクトリ fritzing-0.9.3b.linux.AMD64 をホームに置いて、ホームディレクトリで次のコマンドを実行すると起動します。
$ ./Fritzing
毎回コマンドでは面倒なのでデスクトップにショートカットを作りましょう。テキストエディターで次のようなファイルを作り、Fritzing.desktop というファイル名で保存します。
[Desktop Entry]
Name=Fritzing
Comment=Fritzing
Exec=/home/username/fritzing-0.9.3b.linux.AMD64/Fritzing
Icon=/home/username/fritzing-0.9.3b.linux.AMD64/icons/fritzing_icon.png
Terminal=false
Type=Application
以前自分で作成したパーツデータは .fzpz 形式ファイルにエクスポートしてありましたので、~/Documents/Fritzing/parts/user に置いて Fritzing から開いてやるとインポートできます。
これで回路図の作成ができるようになりました。
Fritzing は 回路図とともに Arduino スケッチも保存できるので、とても便利ですね。
]]>
Arduino IDE を Arduino - Software からダウンロードします。
ファイルマネージャーでダウンロードした arduino-1.8.9-linu64 を右クリックし展開します。展開されてできたディレクトリ arduino-1.8.9 をホームに置いて、ディレクトリに入ります。
何もないところを右クリックして「端末で開く」、次のコマンドを実行します。
$ ./install.sh
えーっと、途中なにやらエラーとか出てたような気がしますが、まぁ適当に処理してください。問題なく動けばとりあえずそれで良いかな、と (^_^;)
デスクトップに arduino-arduinoide.desktop ができるのでダブルクリック。「信頼して起動」すると Arduino IDE のアイコンに変わり、Arduino IDE が起動。シリアルポートを /dev/ttyACM0 に設定しておきます。
シリアルポートはグループが dialout なので、
$ ls -l /dev/ttyACM0
crw-rw---- 1 root dialout 166, 0 4月 13 18:27 /dev/ttyACM0
ユーザー username を dialout グループに追加します。
$ sudo usermod -a -G dialout username
これで IDE から Arduino へ書き込みできるようになりました。
]]>
こいつで PSO2 を起動すると、簡易設定のグラフィック設定が最大の「6」でもちょっとギクシャクする場面はありますが、現在使っているパソコンと変わりない動きをします。もちろんグラフィックは、いままで見たことのない世界が広がりますが (^_^;)
標準的な設定は「3」とのことなので、ちょっと欲張って「4」にして遊んでみようと思います。
で、ちょっとやってみた感想。楽しいわ (^_^;) こうしてだんだんハードウェアにものめりこんでいくんだろうなぁ。
ちなみに現在ハンター Lv.75 、ファイター Lv.75 、レンジャー Lv.75 。フォースが Lv.56 でレベルアップ中です。フォースが Lv.75 になればヒーローになれるのかな? 頑張りましょ。
]]>
まずは Windows10 64bit をインストールします。毎度のようにインストール DVD から実行していけば、問題なくインストールできますね。難しいことはありません。
インストールが完了したら「ディスクの管理」を起動し、C ドライブにあたるパーティションを縮小します。約 260GB に縮小できたので、ubuntu 用に約 100GB 、共有パーティションとして約 130GB が確保できました。共有パーティションは Windows10 と ubuntu とで共有して使う場所で、作っておくとなにかと便利です。
次は ubuntu18.04 LTS をインストールします。こちらは 64bit のみですね。
Windows とのデュアルブートも何度もやっていることなので、いつものようにインストール DVD から起動して進めていきます。が、あれ?インストールボタンをクリックするとブート領域がなんとかかんとか文句言ってきます。何か間違えているのかと悩むこと一晩 (^_^;)
原因は、わかってしまえば簡単なことなのですが、ubuntu のインストール DVD をレガシードライブから起動していた、とゆーことです。
Windows10 64bit をインストールすると UEFI になります。俺の古い頭は、この UEFI というものをよく理解していない。UEFI のパソコンで DVD から起動するためには BIOS でレガシーモードにしなければならない、という程度の知識。今回起動モードをしっかり見てみたら UEFI の DVD ドライブというものが存在していました。このドライブを選択して起動すると UEFI でインストールされ、ブートは EFI が管理する、と。まぁ今回はその程度で (^_^;) 機会をみつけて勉強しましょ。
ちなみに UEFI で起動するとブートローダが起動、レガシーだと「Ubuntuを試す」の選択画面が表示されます。
さて、ブートローダが起動したら Install Ubuntu を選択しインストールを進めます。基本的にはこれまでと変わりありませんね。難しいことはないです。
インストールは「日本語」、キーボードレイアウトも「日本語 - 日本語」を選択します。
アップデートと他のソフトウェアでは「最小インストール」、その他のオプションのチェックはすべて外します。必要なものは後からインストールするほうが勉強になりますよ (^_^;)
インストールの種類は「その他」です。ubuntu 用のパーティションは /dev/sda5 、すべて「 / 」とします。swap 用のパーティションは作りません。共有パーティションは /dev/sda6 で、「 /Share 」などとしておくとディレクトリが作られます。
/dev/sda5 ext4 初期化 マウントポイント /
/dev/sda6 fat32 初期化 マウントポイント /Share
ウインドウが画面外へはみ出しているので「インストール」ボタンが見えません。super キー ( Windows キー) を押しながらドラッグするとウィンドウが移動できますので、上のほうへ移動して「インストール」を押します。
あとはタイムゾーン、ユーザー名、パスワードなどを設定。インストールが完了したら「今すぐ再起動」。
ubuntu のブートローダが起動したら、ubuntu、Windows10 がそれぞれ起動できることを確認して、完成です。
]]>
なので、SSH でリモート操作することにします。
その前に、Wake on LAN でサーバーが起動できることを確認しましょう。起動するためにサーバーのところへ行くなんて意味不明ですからねぇ。
MAC アドレスを ifconfig で確認して、gWakeOnLan などのアプリケーションからマジックパケットを送出すると… 起動できました。起動できないときは BIOS を確認するなどしてみましょう。
さて、SSH サーバーをインストールします。
$ sudo apt-get update
$ sudo apt-get install openssh-server
SSH が起動しているかどうかは
$ service --status-all | grep ssh
で確認できます。
暗号鍵を置くディレクトリを作っておきます。
$ mkdir ~/.ssh
クライアント (ubuntu 18.04.2 Desktop) 側で暗号鍵を生成して、公開鍵をサーバーへ送ります。
$ ssh-keygen -t rsa
$ scp -P 22 ~/.ssh/id_rsa.pub username@IP-address:~/.ssh/id_rsa.pub
サーバー側で公開鍵を authorized_keys に登録 (追加) します。初めから authorized_keys という名前でコピーしちゃうと、すでにファイルがあった場合に困っちゃいますから、追加するという方法で実行しています。
$ cat id_rsa.pub >> authorizes_keys
パーミッションを変更しておきます。
$ chmod 600 ~/.ssh/authorized_keys
クライアントから SSH で接続できることを確認します。
うまく接続できたら、最後に設定ファイルを書き換えましょう。
$ sudo vi /etc/ssh/sshd_config
変更するのは以下の 3ヶ所です。コメントアウトされているので、行頭の # も外します。
PasswordAuthentication no
Permit Root Login no
PermitEmptyPassword no
SSH サーバーを再起動します。
$ sudo /etc/init.d/ssh restart
これで普段使っているクライアントパソコンからサーバーのメンテナンスができるようになりました。
]]>
ハードディスクは 500GB を 2 台取り付けて RAID1 とします。
OS は ubuntu server 18.04.2 LTS 64bit ですが、インストーラは新しい live 版ではなく、従来型の alternative installer を使用します。
その他、基本的なインストール方法は「Ubuntu ServerをExpress5800/110Geにインストール」と同様でした。RAID1 の設定については、まぁやっぱりよくわからなくて何回かやり直したのですが、こんな感じが完成形です。18.04 ではスワップはファイルになるそうで、以前のようにパーティションを分けることはしないようです。
RAID1 device #0 - 500.0 GB Linux Software RAID Array
#1 500.0 GB F ext4 /
SCSI1 (0,0,0) (sda) - 500.1 GB ATA ST500DM002-1BD14
#1 primary 500.1 GB K raid
SCSI2 (0,0,0) (sdb) - 500.1 GB ATA ST500DM002-1BD14
#1 primary 500.1 GB K raid
とゆーことで、とりあえず OS のインストールは完了。アップデートして最新の状態にしておきましょう。
RAID の状態は
$ cat /proc/mdstat
で確認できます。[UU] となっていれば正常。resync の数値が同期の進捗状況で、完了すると表示がなくなります。
]]>
ググってみると「Windows 10 のアップグレード後、明るさの調節ができない - DO」という lenovo のサイトを見つけました。ところが調べてみると、G575 というこの機種は Windows 10 に対応していないらしい。なので Winodws 10 用のドライバーが無い (^_^;)
それならば物は試し、Windows 8 用のドライバをインストールしてみましょう。
ホーム - グローバルサポート - EG から「AMD グラフィックス ドライバー Windows 8 (32bit, 64bit)」をダウンロードしインストールしてみました。インストールの終わりごろにディスプレイの明るさが設定してある 50% に変化。明るさの調整ができるようになりました。
ところが、スタートボタンを押すとタイルの枠は表示されるのですが、文字などが表示されない状態になっていました。
再起動してみようか… あぁ、電源のアイコンが無い (^_^;)
まぁ慌てることはありません、スタートボタンを右クリックすればメニューが出ます。ということで、再起動してすべて正常になりました。
]]>
まずは HDD をチェックしましょう。
linuxBean をライブ起動し、ターミナルから S.M.A.R.T. テストを実行します。結果は「PASSED」。不良セクターもありませんでした。稼働時間は11,000時間あまりですので、まだまだ使えますね。
搭載メモリーが 2GB なので、Windows 10 は 32bit にします。バージョンは 1803 。1809 をダウンロードすればいいのですが、まぁ面倒なのでこのままいきます (^_^;) クリーンインストールです。
インストール DVD を挿入して起動。プロダクトキーはとりあえず「ありません」としておきます。インストールする OS は「Windows 10 Home」、種類は「Windows のみをインストールする」を選択します。インストールする場所の選択ですべてのパーティションを削除します。ここが大事なポイント。
すべて削除したら、今度は新規作成します。自動的にパーティションが 2つ生成されますので、パーティション 2 (システムで予約済みでない、容量の大きい方) を選択してインストールを進めます。
インストールは正常にできたようですので、指示に従って初期設定をします。
アカウントは「オフラインアカウント」を選択します。しつこく Microsoft アカウントを聞いてきますが、断固として (^_^;) ローカルアカウントにします。パスワードをここで設定するとヒントをいくつも聞かれますので設定しません。あとでアカウントでパスワードを設定すれば、ヒントは 1つで済みます。だいたいヒントなんて必要ありませんから (^_^;)
コルタナは使用しませんので「拒否」、プライバシー設定はすべて「いいえ」とします。
初期設定が完了したら、アカウントからパスワードを設定しておきます。ライセンス認証を求められますので、Windows 7 のプロダクトキーを入力します。つまり、Windows 10 はいまでも無償アップグレードが可能ということです。
ということで、lenovo G575 4383 も無事 Windows 10 に生まれ変わりました。
]]>
機種は lenovo G575 4383 。廉価 PC なので性能もそこそこですが、インターネットアクセスに使うぐらいなら問題ないだろうと考えています。
まず不具合の確認ですが、症状は CD が読み込めなくなったとのこと。
Windows 7 を起動するとコンピューターに CD/DVD ドライブが表示されていません。デバイスマネージャーにも無し。ドライブ自体がまったく認識されていない状態ですが、DVD を挿入すると普通に動作しているように見えます。
そこで BIOS を起動してみると、ドライブは表示されていました。起動ドライブの選択 (起動ロゴ表示時に F12 を押下) にも CD/DVD ドライブが表示されますので、Backup&Recovery 17 Free のリカバリーディスクを挿入してライブ起動してみると、おー正常に起動できましたよ。ハード的な故障ではなくて、Windows から見放されてしまっているのでしょう。
とりあえず HDD を丸ごとバックアップしておきます。妻が「◯◯のファイルがないわよ!」とか言い出したときのためのバックアップです (^_^;)
さて、CD/DVD ドライブが BIOS では表示されているのに、Windows のデバイスマネージャーに表示されない件。
シャットダウンし、AC アダプタなど繋がっているものを全部外し、バッテリーも外します。そしてしばらく放置。バッテリー取り付けて AC アダプタだけ繋いで Windows を起動すると、なんと CD/DVD ドライブは見事に復活しました。「電源リセット」というやつですね。
では次は、このノートパソコンも Windows 10 にしちゃいましょう。
]]>
なお、一連作業の顛末であって技術的解説ではありません。あしからず。
現在取り付けられている HDD は、Windows のチェックディスクでは不良セクターなどの異常はみられません。これまでの一連の作業中にも異常を感じるような動きもありませんでした。
でも S.M.A.R.T. テストを行なってみると「Completed: read failure」となり中断してしまっていることがわかります。まだすぐに問題がでるレベルではないようですが、リードエラーとシークエラー、ペンディングセクターが検出されています。稼働時間は 25,000 時間を超えていますので、そろそろ交換したほうが良い感じですね。
Amazon で購入した中古 HDD の S.M.A.R.T. テスト結果は「Completed without error」でした。稼働時間は 16,600 時間余り。リードエラーやシークエラー、不良セクターはまったく検出されていません。問題なく使用できそうです。
ということで、HDD を交換することにしましょう。
まず、Backup&Recovery 17 Free で HDD を丸ごとバックアップします。そして HDD を交換し、リストアします。今回は 500GB から 320GB へ容量が減少しますが、使用容量はほとんど OS 分だけですので問題はありません。
リストアが済んで正常に起動できたら、HDD の交換は完了です。
現在の妻のパソコンからユーザーデータを引っ越ししますが、こいつがけっこう時間がかかる。ローカルディスクにいろいろ貯め込むと、こういうときに苦労するんですよね (^_^;)
さてと、そんなこんなで中古 FMV-BIBLO は無事、妻への誕生日プレゼントとなったのでした。
]]>
では、この Windows 7 を Windows 10 にアップグレードしてみましょう。
なお、作業一連の顛末であって技術的解説ではありません。あしからず。
まず、工場出荷状態で入っている諸々のおまけソフトはアップグレード前に整理しておきたいですね。削除できるものは削除。システムに関わるユーティリティなんかは削除すると具合が悪くなることがあるので、スタートアップで無効にする。など。
できるだけすっきりした形でアップグレードしたほうがいいんじゃないかなぁと、思ってます。
削除とかして具合が悪くなったら、バックアップをリストアしてもとに戻す。そのためにもバックアップはとても役に立ちます。
アップグレードは Windows 10 のダウンロード から MediaCreationTool1809.exe をダウンロードし、実行します。
以上です。簡単でしょ (^_^;)
ってことで Windows 10 にアップグレードを開始。
と、ディスプレイに互換性がない、と注意されました (^_^;) ここまできて諦めるのは… とりあえず「確認」ボタンを押して進めてみます。
順調にインストールが進み再起動。が、ここで「0xC1900101 - 0x20017」エラーが発生しました。
んー、ちっとも簡単じゃねーじゃねーかっ。
ググってみると、BIOS で USB のレガシーサポートを「使用しない」にしてアップグレードすると良いらしい。ってことで、BIOS の設定変更して再挑戦。… 失敗
ワイヤレスマウスを外す。デバイスに PCMCIA なんてのがあったので無効に。生体認証デバイスも無効にして … 失敗
まてよ、ディスプレイに互換性がないというんだから、標準ドライバに変更したらどうなんだ? … 失敗
諦めた (^_^;)
クリーンインストールやってみま〜す。
以前別のパソコンのインストールに使った Windows 10 64bit のインストールディスクがあったので、これを挿入して起動。現在のパーティションはすべて削除し、新規に作成してインストールを開始します。「現在のパーティションを全部削除する」というのが大事なポイントです。ここではとりあえずプロダクトキーなしで進めました。
… と、何の問題もなくインストール完了。ディスプレイの互換性? ドライバー? エラー表示? なにそれ? って感じ。長時間かけて Windows 7 を復活させたのって何だったんだ?
プロダクトキーは Windows 7 のもので認証できました。バージョンは 1803 です。現在 Windows Update してます。
妻が「私のパソコン調子悪いし、これちょうだい」と宣う。まっいいか、妻への誕生日プレゼントにしよう (^_^;)
]]>
なお、修理一連の顛末であって技術的解説ではありません。あしからず。
症状は、Windows7 の起動中に固まってしまうというもの。スタートアップ修復が起動するけど、修復できませんと文句たれる。linuxBean をライブ起動させてユーザーファイルは救い出せたので、マザーボードもHDDも生きているんじゃないのかな?
といった感じだけど、まぁねぇ、10年使ったパソコンだとお払い箱になりますよねぇ。
でも、ノートパソコンで Core 2 Duo で、液晶ディスプレイも綺麗で、マザーボードに問題なさそうで、ときたらこのまま処分するのはもったいない。俺が仕事で使うには十分な子ですよ (^_^;)
まずHDDのテストをしてみましょう。
linuxBean をライブ起動させて S.M.A.R.T. テストを実行しましたが、read failure で中断されました。不良セクタが検出されているようです。
で、この時点で Amazon で 2.5 型内蔵 HDD 320GB の中古品を注文しました。1,098円也。ついでに以前からほしいと思っていたワイヤーストリッパーも合わせて注文して、送料無料となりました。
閑話休題。
HDD が届くまで、もう少し進めてみましょうか。
リカバリ用のイメージファイルが HDD にあるようなので、C と D ドライブ以外の 2つのパーティションを Backup&Recovery 17 Free を使ってバックアップしておきます。あとでうまく利用できるかどうかは定かではありません (^_^;)
C ドライブをリカバリしてみると、すんなり正常終了。問題なく Windows 7 Home Premium が起動しました。
チェックディスクしてみると、C も D も不良セクタは検出されません。でも S.M.A.R.T. テストは read failure となります。
ここで HDD 全体をバックアップしておきました。たしか Backup&Recovery は、HDD に異常があるとバックアップできなかったと思うのだけど、これも正常終了しました。整合性チェックも問題なしです。
なんだかこのまま問題なく動いちゃうんじゃないの? 注文した HDD は、無駄になるならそれはそれでいいのだけど (^_^;)
Windows 7 を最新の状態にするために Windows Update を実行しましょう。
1 回目の更新が完了して再起動すると、Windows 10 にアップグレードしなさい、とゆー全画面表示が出ました。左下に小さく (^_^;) 「Windows 7 SP1 に更新」とありましたのでここをクリック。
2 回目の更新で SP1 がインストールされました。
3 回目の更新プログラムの確認を実行すると「8007000E 不明なエラー」が発生。いつもお世話になっている ぼくんちのTV 別館 を参考に、
を順次実行しました。1. 2. を実行しても改善せず、そのまま 3. のツールを実行し改善しています。
ここで更新プログラムが 175 個出てきました (^_^;) すべてインストールされるまで、じっくり待ちましょう。さらに何回か更新を繰り返し、更新プログラムがなくなるまで行ないます。
ちなみに、ここまでの作業で HDD の不具合と思われるような現象はまったくありませんでした。
すべての更新が終わったら、また HDD 全体をバックアップしておきます。
さて、これでお払い箱になって俺のもとへ来たノートパソコンは Windows 7 Home Premium 機として蘇りました。
]]>
そういえば、昔々、Yahoo! ジオシティーズでホームページ作っていたっけ。
まぁ古いものだし消えてもいいか…
あれ、わが家に最初に Bフレッツを導入した時の話が載ってるけど…
これ、ある意味貴重な記録だよなぁ…
とゆーことで、アーカイブとして保存することにしました (^_^;)
使用したソフトは HTTrack です。これでサイトのミラーを作成します。そんだけ。
ちなみにわが家に初めて Bフレッツを導入したのは 2002年11月、今から16年も前のことでした。懐かしいなぁ。ISDN の 64Kbps が FTTH の 100Mbps になった感動を思い出すよ。でも、ルーターは ISDN 用を改造して使っていたので実質のスピードは 1.5Mbps ぐらいだったとゆーオチ (^_^;)
ってことで、meyon's STUDY の歴史もさらに重ねられていくことでしょう。どうぞよろしくお願いいたします。
]]>いろいろ原因を調べているうちにアースラインが 0.1V になってることに気づきました。いくつものブレッドボードを繋げていたので、どこかで接触不良がおきているのではと考えて調べると、大元の電源回路からアースラインにつないでいるジャンパーピンでの接触不良でした。
この部分、サンハヤトのブレッドボードにサンハヤトのジャンパーピンを使っていましたが、まぁブレッドボードの信頼性ってこの程度のものなのだなぁと認識したわけで。ちなみに最近はもっと安いブレッドボードを使っています。
閑話休題
ジャンク基板から取り外した表面実装 LED があったので、専用の基板 (*1) に載せて電源表示用の LED 基板を作ってみました。よく電源をいれたまま作業してしまうので、注意喚起用ですね。
(*1) 秋月電子通商 チップLED変換パッド(12枚入)
手ハンダなので LED が斜めになっているのはご愛嬌です (^_^;) 左下にチップ抵抗 (これもジャンク品) 330Ω をつけてありますので、5V ラインに繋ぐだけで点灯します。
さすがにこのサイズになると、老眼とか手の震えとか、高齢者には辛い工作です。といいつつ、2 個作っちゃいましたけど (^_^;)
]]>
Z0 から Z7 の 8 個の押ボタンを押してそれに応じた 3 ビットのデータ A0 A1 A2 で LED を点灯させます。2 つ以上の押ボタンを同時に押した場合は数値の大きい方を優先させます。どの押ボタンも押されていないときは E を出力します。
真理値表は次の通り
入力 | 出力 | ||||||||||
Z7 | Z6 | Z5 | Z4 | Z3 | Z2 | Z1 | Z0 | A2 | A1 | A0 | E |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 0 | 0 | 1 | - | 0 | 0 | 1 | 0 |
0 | 0 | 0 | 0 | 0 | 1 | - | - | 0 | 1 | 0 | 0 |
0 | 0 | 0 | 0 | 1 | - | - | - | 0 | 1 | 1 | 0 |
0 | 0 | 0 | 1 | - | - | - | - | 1 | 0 | 0 | 0 |
0 | 0 | 1 | - | - | - | - | - | 1 | 0 | 1 | 0 |
0 | 1 | - | - | - | - | - | - | 1 | 1 | 0 | 0 |
1 | - | - | - | - | - | - | - | 1 | 1 | 1 | 0 |
真理値表から論理式を書くと次のようになります。
A2 = Z7・Z6・Z5・Z4 + Z7・Z6・Z5 + Z7・Z6 + Z7
A1 = Z7・Z6・Z5・Z4・Z3・Z2 + Z7・Z6・Z5・Z4・Z3 + Z7・Z6 + Z7
A0 = Z7・Z6・Z5・Z4・Z3・Z2・Z1 + Z7・Z6・Z5・Z4・Z3 + Z7・Z6・Z5 + Z7
E = Z7・Z6・Z5・Z4・Z3・Z2・Z1・Z0
これを簡略化しますが、計算をここに書くのは大変なので m(_ _)m 結果だけ。
A2 =Z7 + Z6 + Z5 + Z4
A1 = Z7 + Z6 + Z5・Z4・Z3 + Z5・Z4・Z2
A0 = Z7 + Z6・Z5 + Z6・Z4・Z3 + Z6・Z4・Z2・Z1
E = Z7・Z6・Z5・Z4・Z3・Z2・Z1・Z0
そして描いた回路図が次です。
「Fritzing ロジックゲートパーツを作成する」で、自分で作ったゲートのシンボルを使いました。
入力はプルダウンしバッファを通しています。インバータは NAND ゲートを使用していますが、手持ちのロジック IC を利用したというだけです。OR ゲートや AND ゲートも手持ちが 2 入力だけでしたのでそれらを利用しています。NAND ゲートだけで組み立てるってことも、もちろん可能です。
LED 駆動回路は毎度の KSC1815Y (2SC1815Y 互換品) のスイッチング回路です。E を表示する LED だけ高輝度なので抵抗定数が異なっています。まぁ手持ちのパーツで気楽に適当にやりましょうってことで (^_^;)
ブレッドボードです。
左のタクトスイッチを押すと、それに応じた値を右の緑の LED で 3 ビット表示します。何も押されていないときは赤の LED が点灯します。タクトスイッチを 2 つ以上同時に押すと、値の大きい方が優先されます。
]]>
そんなこんなの試行錯誤の備忘録です。他の人に教えようとかゆー考えがあまりないので、以下もやっぱりわけわからん内容かもしれませんが、あしからず m(_ _)m
また、俺は回路図しか利用しないし作るのはゲートなので、ブレッドボードやプリントパターンのパーツは作りません。基本は同じですけどね。
パーツの作り方は本家の Creating custom Parts を参照のこと。
Fritzing を使えることは大前提。その上で、
という 2つの作業を行ないます。
そのためにまず Inkscape をインストール。俺のパソコンは ubuntu なので、「ubuntu ソフトウェア」で検索すると出てきますから「インストール」をポチッ。もちろん、Inkscape 以外でも svg 形式で画像を保存できるソフトなら可です。
1. Inkscape でパーツをデザインする
グラフィックに関することは本家の Fritzing's graphic standards を参考にします。
色はパーツを RGB(0,0,0) 、コネクタやラベルは RGB(85,85,85) にします。フォントは Driod Sans が指定されているけど、「オブジェクトをパスへ」操作をすれば何でも良い。ちなみに ubuntu には Droid Sans Fallback ってのがあるけど、使えるかどうかは確認していません。
文字サイズはパーツ名 4.25pt 、ピンラベル 3.5pt 、ピン番号 2.5pt 。コネクタの間隔は 0.1inch 、太さ 0.7pt 、長さ 7.2pt 。といったところが指定されています。
色や文字サイズは好きにしてもいいけど、コネクタについてはサイズを守る必要があります。そのために、Inkscape の「ドキュメントのプロパティ」でページサイズを in (インチ) に替えておく。グリッドの単位も in にしておく。インチでデザインしないと Fritzing に載っけたときにコネクタ位置が合いません。
下は Inkscape で AND ゲートを作っているところ。使い方は試行錯誤しているうちになんとなくわかってきます (^_^;) もちろんグラフィックソフトの一般的な知識 (例えばレイヤとか) は必要でしょうね。デザインの細かいコツなんかはグーグル先生に教えてもらってください。ちなみに俺が参考にしたことのひとつは、コネクタの端形状は丸にしておくってこと。ラインとの繋がりがきれいになるらしい。
デザインができたら次を実行します。
以上でパーツデザインができ上がりました。
2. Fritzing でコネクタの設定をする
Fritzing でパーツを作るには parts editor を使用します。
まず、新たに作るパーツに類似した既製のパーツを選びます。今回はゲートなので Core Parts の 74HC595 にしました。例えば抵抗でもかまわないのですけど、Metadata があまりに異なると修正が面倒です (^_^;) まぁ自分で回路図に利用するだけなら修正する必要もないんですけど。
選んだパーツを右クリックして「Edit Parts」をクリックするとParts Editor が起動します。
(*注) 接続点の移動について
下図のように表示されているコネクタの白い点線の枠の中央の縦線部分が接続点で、ここにワイヤが繋がります。これをワイヤを接続したい左端へドラッグして移動します。なお、画像がコネクタリストで選択されていると、画像自体が動いてしまってうまく移動ができません。ドラッグする前に画像の外側を一度クリックして選択を解除しておくとうまくいきます。
これで MINE に新しいパーツが登録されました。実際に回路図を描いてみて、自分なりにでき上がりを評価してみましょうね。
]]>