はじめに
これは私の職場での出来事です。
現場のおっちゃん(以下現場)
「〇〇君(私)、PLCプログラムをいじって製品が流れてくるたびにブザーを鳴らしてくれんかの?」
私「?。別に可能ですが、うるさくないですか?製品は早くても5秒おきに流れてきますから、生産中ずっと5秒ごとにブザーなりますよ?」
現場「上流側でトラブルが起きたときに製品が流れてこないから、その間に材料の補充やメンテナンス、新たな条件設定とかやっておきたいんだけど、その時に生産が再開すると製品が流れてきたことに気付けないんだよね。」
私(じゃあ、上流側で生産が再稼働したらブザー鳴らすようにしよう。)
と考えたのが今回の投稿内容のきっかけです。
完成目標
本当は設備PLCのプログラムに追加をしてもよかったのですが、設備についているブザーでは決まった音階しか流せず長さでしか差別化できないため、他の異常ブザーなどと判別できなくなる不安があったのと、空いているピンが少なかったので最近研究中のマイコンで実現しようと思いいたりました(単に自分がやりたいだけだったりする、、、)。
またプログラム内容が単純でM5stamp C3Uでも過剰スペックだと感じたので、最近ハマっている格安マイコンCH32Vで実現しようと思います。
(趣味の範囲内なので自己判断で自費購入して検証しています)
前回の投稿でCH32Vを使用したタイマーリセットプログラムを作成しました。これをLEDからブザーに置き換えて実現しようと思います。
前回の投稿
https://fa-vivorock-mura.com/ch32v003j4m6-1/
今回使用するもの
- M5stamp C3U
最初にオルゴールプログラムの検証として使用しました。
|
- CH32V003J4M6
秋月電子通商で販売しています。
https://akizukidenshi.com/catalog/g/g118062/
- マイコンDIP変換基板および基板用リードフレーム
CH32V003J4M6の販売ページに適した製品のリンクが掲載されています。それを選べば失敗はしないです(秋月マジ便利)。マイコンのDIP変換基板は「SOP8(1.27mm)DIP変換基板」というものを選び、基板用リードフレームはブレッドボードに差し込めるものなら何でもいいと思います。
- WCH-LinkEエミュレーター
CH32Vシリーズマイコンにプログラムを書き込むなら必須アイテムになります。こちらもCH32V003J4M6の販売ページに製品リンクが掲載されています。
https://akizukidenshi.com/catalog/g/g118065
- 押しボタンスイッチとブザー
ブザーに関しては自励式と他励式の2つがあり、ここでは他励式を選んでください。自励式では今回のプログラムが実現できません。またブザーには極性の有るものと無いものがありますがどちらでもいいです。
他の仕様は電圧、電流があっていればなんでもいいです。
M5stamp C3Uプログラム
電子ブザーを使用したプログラムが今回初めてだったので、まずM5stamp C3Uで実現してみました。
簡単にドレミを演奏するプログラムです。
int melo = 200; // 音の長さを指定
int pin = 8; // ブザーを接続したピン番号
void setup() {
}
void loop() {
tone(pin,262,melo) ; // ド
delay(melo) ; // 音がなっている間待機
tone(pin,294,melo) ; // レ
delay(melo) ;
tone(pin,330,melo) ; // ミ
delay(melo) ;
delay(1000) ; // 1秒待機
}
Arduino ideではtone関数というものがあり、これの()に(ピン番号, 鳴らしたい音源の周波数, 音の長さ)を書き込むことで狙いの音階と長さを鳴らすことができます。この関数はpwmを用いたものなので使用するマイコンもそのピンもpwm機能が有効なものを選んでください。今回私が使用したM5stamp C3Uは5VやGND等を除くGPIOピンなら全部pwmに対応しているみたいなので、ここではGPIO8を使用しましたが他でも可能です。
狙い通りドレミと演奏し1秒間空いたのち繰り返す動きを実現できました。異なる音階を演奏するため設備で使用しているブザーとも区別できそうです。
(茶番)CH32Vプログラム?
ドレミプログラムはできそうだとわかったので、このプログラムをCH32Vにも書き込んで実現します、、、、、、、、、。
と言いたいところだったんですが、実はCH32VプログラムはArduino ide環境ではまだ開発途中ということで、2024/4/27時点ではまだpwmを使用したプログラムは組めません。よってtone関数もコンパイルで認識せずエラーが発生しました。
ch32v003funという別の開発環境であればpwmにも対応していると聞いたのですが、C++、microPython、Raspberry PiのPythonとすでに3種類くらいプログラム開発環境と端末があり、これ以上増えると管理とか頭がパンクしそうになるので開発環境は増やしたくなかったです。CH32Vでtone関数を使ったプログラムは諦めます。
いかがでしたか?調べた結果、CH32Vではできませんでしたぁ、、、、、、。
で終わってしまうとただのダメブログになってしまうし、もともとこの用途の実現のためにCH32Vのプログラムを始めた側面もあり、何より自分に負けた気がするのでもう少し足掻いてみます。
改めてCH32Vプログラム | まずはドレミ
とりあえず作戦です。tone関数で音階を演奏する仕組みについて説明します。
下図のようにONとOFFのパルス信号を出力することで音を発生させています。1周期内のONとOFF時間は同じです。この1周期の時間を変えることで人間の耳には異なった音階として認知できます。
つまりはピン出力されるONとOFFの時間を制御すれば音階を変えられる、と予想しました。
ようは本来はtone関数1文で済むプログラムを自作するということです。結構強引な気もしますがこれでプログラムを組んでみました。
まずM5stamp C3Uで実現したプログラムと同じ出力結果になるようなプログラムを組んでみました。
さっきと同じドレミを演奏するプログラムです。
//tone_selfmake.ino
void setup() {
pinMode(PD6, OUTPUT);
}
//f()の中に以下の数字を入れる
//ド:239 レ:213 ミ:190
//各音階は0.2秒間発生
void loop() {
int a=0; //音の発生時間をリセット
while(a<200){
int y=f(239);
a++;
delay(1);
}
while(a<400){
int y=f(213);
a++;
delay(1);
}
while(a<600){
int y=f(190);
a++;
delay(1);
}
delay(1000); //演奏終了後1秒間無音
}
//音階制御関数
int f(int n){
digitalWrite(PD6, HIGH);
delayMicroseconds(n);
digitalWrite(PD6, LOW);
delayMicroseconds(n);
return;
}
音階制御関数というのは入力された音階の1周期の時間に合わせてONとOFFを1周期分実行する自作関数です。
int y=f(239)
の()に数字を入れることで発生する音階を変更します。変数aで音を出す長さを制御しています。変数aの数字を0.001秒ずつに1カウントアップすることで0.001秒ごとに音階制御関数を繰り返しています。a<200では0.2秒間音階制御関数を繰り返すことになります。
ここでは「ド:239 レ:213 ミ:190」としています。
この数字の出し方について説明します。
- まずネットで各音階の周波数を検索します。
- その後周波数を周期に変換する計算を行います。私は以下の計算サイトにて自動計算をさせていただきました。
参考にした計算サイトhttps://keisan.casio.jp/exec/user/1341380413#! - その後求めた周期を÷2します。これはON時間とOFF時間を分けるためです。
- 求めた数値をプログラムで使用します。
回路図は以下のように配線しました。
結果としては、若干の聞き取りにくさはあるものの各音階の区別はできるように発音できました。
今回使用したドレミの周波数は国際式表記ではC7, D7, E7です。これは高音の部類に入るものですが、当初低音で試したところほとんど同じ音に聞こえるくらいのレベルでした。いろいろ試した結果高音のほうが聞き取りやすかったためです。
CH32Vプログラム | 本来の目的
では先ほどのプログラムを前回の投稿のプログラムと合体させ、当初目的のプログラムを作ります。デバック目的としてArduino ideのシリアルモニタに文字列を出力させるプログラムも入れてます。
シリアルモニタ使用時は通信速度(ボーレート)を115200bpsに設定してください。
プログラムと回路図は以下のようになりました。
//timereset_doremi.ino
byte countdowntime;
void setup() {
delay(5000);
Serial.begin(115200);
pinMode(PC2, OUTPUT);
pinMode(PC1, INPUT_PULLDOWN);
countdowntime = 10;
Serial.println("CountDown Start!");
}
void loop() {
if(countdowntime>0){
bool in = digitalRead(PC1);
if(in == HIGH){
countdowntime = 10;
Serial.println("CountDown reset");
}else{
countdowntime--;
Serial.println(countdowntime);
delay(500);
}
}else{
Serial.println("melody time");
int a=0;
while(a<200){
int y=f(239);
a++;
delay(1);
}
while(a<400){
int y=f(213);
a++;
delay(1);
}
while(a<600){
int y=f(190);
a++;
delay(1);
}
countdowntime = 10;
}
}
int f(int n){
digitalWrite(PC2, HIGH);
delayMicroseconds(n);
digitalWrite(PC2, LOW);
delayMicroseconds(n);
return;
}
今回プログラム書き込み時とシリアルモニタ使用時で配線を変える必要があります。
シリアルモニタを使用するには1ピンと8ピンをそれぞれエミュレーター側の4ピンと2ピンに接続しました。前のプログラムでは1ピンをPD6として使用していたため、6ピン(PC2)にプログラムと配線を変更しました。
そしてシリアルモニタを使用するにはプログラム上にSerial.begin(115200);
を記述してやる必要がありますが、このままでは次のプログラムが書き込めなくなり、このマイコンがおっちゃん要望用途専用機になってしまいます。そこでその文より前にdelay(5000);
を追加してやることでプログラム書き込みのための時間稼ぎを行っています。この手法は以下のサイト様を参考させていただきました。
参考サイトhttps://qiita.com/Ke_N_551/items/af7f3563f91fef542c69
結果としては狙い通りの動きができました。ボタンを押すとカウントダウンがリセットされ、10秒のカウントダウンが達成するとドレミを1回鳴らす動きです。シリアルモニタの表示は下の通りです。
ただし前回の投稿でもあったようにボタンの感度が悪く1秒以上押さないと検知できない問題はあります。ボタンを押すとシリアルモニタ上では下のように「CountDown reset」が連続して出力されます。
今回のプログラム書き込み量は81%と、CH32Vのプログラム容量とピン数をぎりぎりまで活用できました。
まとめ
Arduino ide環境ではCH32Vマイコンでtone関数が使えないとわかったときは半ば諦めていましたが、思い付きで始めたプログラムが思いのほかうまくいってよかったです。
現場の配線では24Vの電源とセンサ出力を使用することになりそうなので、どこかで降圧およびリレーをはさむ必要がありそうです。
ともあれ、これで現場のおっちゃんを助けられます。