2016年1月27日水曜日

お風呂時計(第三回・小改良)

■少しだけ改造■

はい、続きです。


電池が弱くなると実際より明るく判断してしまう傾向があるようで、風呂場の照明を切っていても脱衣場を点灯すると時計のスイッチも入ってしまいます。

また、ちょっと向きを変えたりした時に手で影ができると計時が止まってしまい、入浴時間ゼロに戻ってしまいます。

ということで、時計が点灯中は、暗くなったかどうかの基準値をより小さくします。つまり、一度点灯したと判断したらかなり暗くしないと止まらないよ、という改造。今までは明暗の判断を1つの数値(45)で行っていたのですが、今回、明るくなったという判断には60、暗くなったという判断は50にしてみました。これで使用中にちょっと影になっても止まらなくなりました。

もう1つ、一度止まっても、30秒間以内に明るくすれば計時をリセットしないようにしました。これでちょっと影になっても、入浴時間がゼロに戻ることがなくなりました。

というわけで、フル充電した電池での開始時間は1/27 22:10です。

追記:終了は3/4 06:00でした。37日と8時間。「暗くなった」という判断値が大きすぎたようで、入浴後に脱衣所からふと風呂場を見ると点灯している、ということがしばしばありました。うーん、しかし、こうやってパラメータをいじって正解にたどり着いたとしても、それは「うちの風呂場で、今のLED電球使っていて」という限定条件ですからね。ここはやっぱり適応制御か何かしないといけない気が。それはそれとして、良いアルゴリズムを思いつくまで、とりあえず70 / 60で使おう←直さない予感 開始:3/4 21:00。

追記の追記:70 / 60だと横にてぬぐいを置いただけで消えてしまい、それはそれで面白いのですが、実用性にかけるので70 / 45にしました。再充電して3/5 08:25再開。

それにしても、こうやってバージョンごとにソースを張っているのはあまりにも頭が悪いので、github使います。

追記^3:あ、記録してなかった。もうちょっとLEDを暗くしたいのですががっちり配線してしまったユニバーサル基板に抵抗入れるのは面倒くさい。ので、デューティー比を変えてみたのですが1:500でもまだ十分明るいです。瞬間何mA流れてるんだろ。5月27日 5時再開。

追記@4:7月6日ぐらいから物凄く暗くなりましたが、7月9日の朝、まだ生きてます。でも、表示読めないしエネループ破損が怖いので止めます。7月9日 06:00。43日で思ったほど伸びなかったのですが、夏になって朝シャワーだけでなく夕方浴槽に浸かるようになったので1日あたりの点灯時間はほぼ倍になっているはず。さすがにデューティー比1:500は暗すぎたので、次は1:200ぐらいにしてみます。7月9日 17:00スタート。明るいw

追記@5:8月21日ぐらいから暗くなり、8月24日ほとんど表示は見えませんが、ここでおしまいにします。1:200だとチラツキが目立つので1:100にしました。デューティー比あんまり電池寿命と関係ないんじゃないか?という気がしてきました(笑)。ということで8月24日5時スタート。

追記@6:いきなり10月2日に消えました。明るいと電池の余力ないですね。でも、明るさと電池寿命はほとんど関係ないですね。精密な微小消費電流計測のできる機材が欲しい。ともあれ、1:100でほぼ支障ないので、そのまま継続。10月4日、電池セット。
#include <sRTC.h>
#include <legacymsp430.h>
#include <APDS9930.h>
// LEDのポート
const int segA = P1_4;
const int segB = P1_5;
const int segC = P2_0;
const int segD = P2_5;
const int segE = P2_2;
const int segF = P2_3;
const int segG = P2_4;
//int segCol = P2_6;
int digits[] = {
P1_0, P1_1, P1_2, P1_3}; // for serial
#define ALL_OFF 10
const uint8_t patA[] = {
HIGH, LOW, HIGH, HIGH, LOW, HIGH, HIGH, HIGH, HIGH, HIGH, LOW};
const uint8_t patB[] = {
HIGH, HIGH, HIGH, HIGH, HIGH, LOW, LOW, HIGH, HIGH, HIGH, LOW};
const uint8_t patC[] = {
HIGH, HIGH, LOW, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, LOW};
const uint8_t patD[] = {
HIGH, LOW, HIGH, HIGH, LOW, HIGH, HIGH, LOW, HIGH, HIGH, LOW};
const uint8_t patE[] = {
HIGH, LOW, HIGH, LOW, LOW, LOW, HIGH, LOW, HIGH, LOW, LOW};
const uint8_t patF[] = {
HIGH, LOW, LOW, LOW, HIGH, HIGH, HIGH, LOW, HIGH, HIGH, LOW};
const uint8_t patG[] = {
LOW, LOW, HIGH, HIGH, HIGH, HIGH, HIGH, LOW, HIGH, HIGH, LOW};
const uint8_t sevenPort[] = {
segA, segB, segC, segD, segE, segF, segG};
const uint8_t *sevenSeg[] = {
patA, patB, patC, patD, patE, patF, patG};
RealTimeClock RTC;
// APDS9930
APDS9930 apds = APDS9930();
uint16_t ch0 = 0;
uint16_t ch1 = 1;
// 最後に表示を更新した時刻(秒)
unsigned long prevSec = 99;
// sprintf用バッファ
char spfBuf[20], buf[20];
unsigned long started;
void setup()
{
// led
for (int i = 0; i < 7; i++) {
pinMode(sevenPort[i], OUTPUT);
digitalWrite(sevenPort[i], LOW);
}
for (int i = 0; i < 4; i++) {
pinMode(digits[i], OUTPUT);
digitalWrite(digits[i], HIGH);
}
// Initialize APDS-9930 (configure I2C and initial values)
if ( apds.init() ) {
// Serial.println(F("APDS-9930 initialization complete"));
}
else {
// Serial.println(F("Something went wrong during APDS-9930 init!"));
}
// Start running the APDS-9930 light sensor (no interrupts)
if ( apds.enableLightSensor(false) ) {
// Serial.println(F("Light sensor is now running"));
}
else {
// Serial.println(F("Something went wrong during light sensor init!"));
}
for (int i = 0; i < 5; i++) {
digitalWrite(P1_0, HIGH);
delay(100);
digitalWrite(P1_0, LOW);
delay(100);
}
RTC.begin();
RTC.RTC_hr = 19;
RTC.RTC_min = 30;
RTC.RTC_sec = 0;
setDigit(0);
}
void setDigit(int d) {
for (int i = 0; i < 4; i++) {
if (d == i)
digitalWrite(digits[i], LOW);
else
digitalWrite(digits[i], HIGH);
}
}
int digit = 0;
int loopCount = 999;
#define MODE_DARK 1
#define MODE_BRIGHT 2
int mode = MODE_DARK;
int isTime = 0;
// #define THRESHOLD 45
#define IS_BRIGHT_LIMIT 60
#define IS_DARK_LIMIT 48
static int duration = 0; // 入浴時間
static int durationDark = 0; // 暗くなってからの時間
static int light = 100; // 直近の明るさ。だいたい100-1000、明かりを消した風呂場で50以下
int lightToTonTime() {
int t = light / 10;
if (t > 90) t = 90;
if (t < 10) t = 10;
return t;
}
void updateLight() {
float f = 0;
apds.readAmbientLightLux(f);
int current = f;
light = (light + current) / 2;
}
void loop()
{
// turn off
setDigit(-1);
delayMicroseconds(100-lightToTonTime());
// チラつきを軽減するために点灯中にセンサーを読みに行く
int sec = RTC.RTC_sec;
if (sec != prevSec) {
if (mode == MODE_DARK) { // mode is Dark
durationDark++;
updateLight();
if (light > IS_BRIGHT_LIMIT) {
mode = MODE_BRIGHT;
BCSCTL1 = CALBC1_8MHZ;
DCOCTL = CALDCO_8MHZ;
// 入浴時間計測開始
if (durationDark > 30)
duration = 0;
isTime = 0;
}
}
else { // mode is Bright
duration++;
if ((duration % 2) == 0) {
isTime = (isTime == 0);
if (isTime) {
updateLight();
if (light <= IS_DARK_LIMIT) {
mode = MODE_DARK;
BCSCTL1 = CALBC1_1MHZ;
DCOCTL = CALDCO_1MHZ;
durationDark = 0;
}
}
}
if (isTime) { // 時刻表示
itoa(10000 + RTC.RTC_hr*100+RTC.RTC_min, buf, 10);
strcpy(spfBuf, buf+1);
}
else { // 入浴時間
int m = duration / 60;
int s = duration % 60;
itoa(10000+m*100+s, buf,10);
if (buf[1] == '0') buf[1] = ' ';
strcpy(spfBuf, buf+1);
}
}
prevSec = sec;
}
// 表示処理
// 対象となる桁の値を0-9か空白に変換しセグメントビットに出力
int num = spfBuf[digit];
if (mode == MODE_DARK) {
out(ALL_OFF, LOW);
setDigit(-1);
}
else {
num = (num >= '0' && num <= '9') ? num - '0' : ALL_OFF;
out(num, LOW);
// 桁ビットを出力(点灯=high)
setDigit(digit);
}
delayMicroseconds(lightToTonTime()); // lightはだいたい1000-100
//次の桁へ
digit++;
if (digit >= 4) digit = 0;
}
void out(int num, int DP) {
for (int i = 0; i < 7; i++) {
digitalWrite(sevenPort[i], sevenSeg[i][num]);
}
}
interrupt(TIMER1_A0_VECTOR) Tic_Tac(void) {
RTC++; // Update secondes
};

0 件のコメント:

コメントを投稿

注: コメントを投稿できるのは、このブログのメンバーだけです。