【SALZminiSolo2】完成しました。SALZminiSoloを元にさらなる改良を加えました。
SALZminiSoloは今も順調に稼働しています。
【SALZminiSolo】(ザルツミニソロ)初お披露目です。順調に稼働しています。
今後、この装置を広めていきたいと思ったときSALZminiSoloはまだまだ改善の余地があります。
一つはコストの問題です。
できるだけ安いものを用いて作れたほうが良いです。
今使っているマイコンはAtom Matrixで1,991円(税込み送料別)です。
もしこれをAtom Liteで置き換えできれば1,287円(税込み送料別)になるため、もっとお手軽に作成することができます。
Atom Matrix
Atom Lite
Atom MatrixとAtom Liteの大きな違いはLEDの数です。
Matrixは5x5のRGB LEDがありますがLiteは1つのRGB LEDしかありません。
LEDの数が少なくなることにより表現力が乏しくなります。
SALZminiSoloでは、水分量の割合をLEDを使って棒グラフのように表示していました。
これがLiteでは使えなくなります。
そこで1つのRGB LEDを用いて水分計の割合を表示するテストを行いました。
まず最初に思いついたのは色による変化です。
水分量が多いと青で少なくなるにつれ、緑、黄、橙、赤と変化させます。
ただこれだけでは不十分だと思いました。
かつて「Inside Macintosh」でカラーアイコンのデザインについて「色のみで変化を表してはならない。」というガイドラインがありました。色の違いを見つけにくい方のための配慮であると同時に、より良いUIとは如何にあるべきかを教えてもらいました。
今回のテーマ、水分量の割合を表す方法を考えるとき、色の変化だけではなく明滅速度を変化させることも行いました。水分計の割合が多いとき、青の表示がゆっくりと明滅します。赤の表示のときは早めに明滅することで色だけに頼らない表示にしました。
ソースコードを掲載します。
// LEDTestAtomLite bbd
#include "M5Atom.h"
#define RGB(r, g, b) (((g) << 16) + ((r) << 8) + (b))
static int pValue = 0;
#define NCOLORS 5
static int colorRs[NCOLORS] = { 0x60, 0x50, 0x30, 0x00, 0x00 };
static int colorGs[NCOLORS] = { 0x00, 0x10, 0x30, 0x50, 0x00 };
static int colorBs[NCOLORS] = { 0x00, 0x00, 0x00, 0x10, 0x60 };
static int delays[NCOLORS] = { 2, 4, 8, 16, 32 };
void task1(void * pvParameters)
{
int a = 0;
int rPrev = 0;
int gPrev = 0;
int bPrev = 0;
while (1) {
int lv = pValue / 20;
a = (a < 360) ? ++a : 0;
float v = (sin(PI * (float)a / 180.0) + 1.0) / 2.0;
int r = (float)colorRs[lv] * v;
int g = (float)colorGs[lv] * v;
int b = (float)colorBs[lv] * v;
char buf[256];
sprintf(buf, "a:%3d lv:%d RGB(%02X, %02X, %02X)", a, lv, r, g, b);
Serial.println(buf);
if (r != rPrev || g != gPrev || b != bPrev) {
M5.dis.drawpix(0, RGB(r, g, b));
}
delay(delays[lv]);
rPrev = r;
gPrev = g;
bPrev = b;
}
}
void setup()
{
M5.begin(true, false, true);
delay(10);
M5.dis.drawpix(0, RGB(0x00, 0x00, 0x00));
// Task 1
xTaskCreatePinnedToCore(
task1, /* Function to implement the task */
"task1", /* Name of the task */
4096, /* Stack size in words */
NULL, /* Task input parameter */
1, /* Priority of the task */
NULL, /* Task handle. */
0); /* Core where the task should run */
}
void loop()
{
if (M5.Btn.wasPressed()) {
pValue = ((pValue += 10) < 100) ? pValue : 0;
}
delay(10);
M5.update();
}
まずは満足できるレベルの物ができました。ボワッと感を出すためにサインカーブを用いて明るさを変えてみました。欲を言えばもっとスムーズにボワーっとしてほしいのですが、カクカク感があります。もともとLEDの明るさ自体、非常に短い時間内で明滅を調整しているはずなので仕方ないかもしれません。今の私の電子工作の実力ではこんなもんです。
SALZminiSoloのソースと組み合わせSALZminiSolo2を作りました。
ソースコードは下記のとおりです。
// SALZminiSolo2 bbd
// for Atom Lite
#include "M5Atom.h"
//デモモード(ON:1, OFF:0)
//#define DEMO_MODE 1
#define DEMO_MODE 0
#define P(col, row) (((row) * 5) + (col))
#define RGB(r, g, b) (((g) << 16) + ((r) << 8) + (b))
#define SECONDS(s) ((s) * 1000)
#define MINUTES(m) SECONDS(m * 60)
const int WAIT_TIME = (DEMO_MODE) ? SECONDS(10) : MINUTES(20);
const int PUMP_TIME = (DEMO_MODE) ? SECONDS(10) : MINUTES(2);
#define INPUT_PIN 32
#define PUMP_PIN 26
static int pValue = 0;
#define NCOLORS 5
static int colorRs[NCOLORS] = { 0x60, 0x50, 0x30, 0x00, 0x00 };
static int colorGs[NCOLORS] = { 0x00, 0x10, 0x30, 0x50, 0x00 };
static int colorBs[NCOLORS] = { 0x00, 0x00, 0x00, 0x10, 0x60 };
static int delays[NCOLORS] = { 2, 4, 8, 16, 32 };
void
DispLevel(int lv) {
pValue = lv;
}
int
WlRate(int wl)
{
//校正済の水分%を返す。
//センサーを水に浸けた状態:1400
//センサーを外に出した状態:2100
const int wetWl = 1400;
const int dryWl = 2100;
int ret = (int)((1.0 - ((float)(wl - wetWl) / (float)(dryWl - wetWl))) * 100.0);
return (ret < 0) ? 0 : ((ret > 99) ? 99 : ret);
}
int
GetWl(void)
{
float val = 0.0;
pinMode(INPUT_PIN, INPUT);
for (int i = 0; i < 10; i++) {
float v = analogRead(INPUT_PIN);
//Serial.printf("ML(%d):%8.3f\r\n", i, v);
val += v;
delay(10);
}
return (int)(val / 10.0);
}
void
PrintData(int wl) {
static int c = 0;
c++;
Serial.printf("SendData():\n");
Serial.printf("1:waterLevel:%d\n", wl);
Serial.printf("2:c:%d\n", c);
}
int
GetDispLevel(void)
{
int ret;
ret = pValue / 10;
ret = (ret > 4) ? 4 : ret;
return ret;
}
//---------------------------------------
void task1(void * pvParameters)
{
int a = 0;
int rPrev = 0;
int gPrev = 0;
int bPrev = 0;
while (1) {
int lv = GetDispLevel();
a = (a < 360) ? ++a : 0;
float v = (sin(PI * (float)a / 180.0) + 1.0) / 2.0;
int r = (float)colorRs[lv] * v;
int g = (float)colorGs[lv] * v;
int b = (float)colorBs[lv] * v;
if (r != rPrev || g != gPrev || b != bPrev) {
M5.dis.drawpix(0, RGB(r, g, b));
}
delay(delays[lv]);
rPrev = r;
gPrev = g;
bPrev = b;
}
}
void setup()
{
Serial.begin(115200);
M5.begin(true, false, true);
pinMode(INPUT_PIN, INPUT);
pinMode(PUMP_PIN, OUTPUT);
M5.dis.drawpix(0, RGB(0x00, 0x00, 0x00));
// Task 1
xTaskCreatePinnedToCore(
task1, /* Function to implement the task */
"task1", /* Name of the task */
4096, /* Stack size in words */
NULL, /* Task input parameter */
1, /* Priority of the task */
NULL, /* Task handle. */
0); /* Core where the task should run */
}
void loop()
{
int wl = WlRate(GetWl());
DispLevel(wl);
PrintData(wl);
Serial.print("wl:");
Serial.println(wl);
if (wl < 30) {
digitalWrite(PUMP_PIN, true);
Serial.println("PUMP ON");
delay(PUMP_TIME);
digitalWrite(PUMP_PIN, false);
Serial.println("PUMP OFF");
}
else
delay(WAIT_TIME);
}
二つ目はケースの改良です。
SALZminiSoloのケースは非常に素晴らしく、Atom Matrixをしっかりと保護しています。ごちゃごちゃするケーブル類も押し込んで上から被せてしまえばOK。ただ、もう少し長さが欲しかった。モーターケースが少しはみ出てしまいます。
100均に行くたびにケースになりそうなものを物色していますが、しっくりきません。
ならば、自作するしかない。ということで、クリアファイルを切り抜いて箱を作るための、展開図を書き始めました。
CADで展開図はすぐ書けるのですが、いつも何気なく使っている包装箱、いざ自分で書くとなると、気にしなければならない点がたくさんあり勉強になりました。お菓子の箱や包装箱をよく観察して、展開図を完成させました。時間の余裕があるときに、包装箱の勉強をしたいと思います。
プリンターで印刷したものをクリアファイルに挟み、ずれそうな所を両面テープで貼ってきました。
最近、苦手ながら工作を少しずつやってて段取りはとても大切なんだとしみじみ思います。
あと、焦らないこと。ゆっくりと時間をかけて丁寧に作ればそれなりに満足できるものができるということがわかってきました。
組み立てて見ます。
うまく行ったかに見えた箱作りでしたが、設計ミスで上蓋のサイズをミスしたり、間違った場所をカットしたりとボロボロです。
ビニールテープで補修し、かぶせてみるとなかなか良い感じです。
ポンプユニットに用途不明のフックがあります。これをなにかに使えないか考えてました。ケーブルを止めるには小さく針金を挟むには大きくてパイプはこのあたりに通す予定はないし扱いに困っておりました。たまたまコンビニでもらう竹箸を挿してみたらシンデレラフィット。安定感が増しました。
あとは、SALZminiSoloと同じつくりです。
【SALZminiSolo】(ザルツミニソロ)初お披露目です。順調に稼働しています。
SALZminiSoloシリーズをじっくり育てていきたいと思います。
最後までお読みいただきありがとうございました。