USB(Aコネクタ)からの電源出力をON/OFFして 過充電を防ぐタイマです。
ArduinoにRTC(RX8900)を接続して、時間を管理しています。
一応 カレンダー表示してますが、制御で使用しているのは RX8900の1sパルス出力だけなので 時間未設定でも使用可能です。
●主な使用部品
No. | 品名 | 品番 | 備考 |
---|---|---|---|
1 | マイコン | ATmega328P | |
2 | RTC | RX8900 | 秋月 K-13009 |
3 | スーパー・キャパシタ | 5.5V 1.0F | |
4 | LCD | 16文字×2行 | |
5 | ロータリーエンコーダ | 秋月 P-05773 | |
6 | USBコネクタ | 秋月 K-07429 | |
7 | FET | 東芝 TK40A06N1 | 秋月 I-13625 |
8 | ポリスイッチ | RUEF250 | 秋月 P-00773 |
9 | ケース | タカチ YM-150 |
●充電タイマの動作
電源を入れると、5つあるUSBコネクタの電源を 1つづつONします。
電源のON時間は 初期値で6時間となっており、各USBコネクタごとに 時間を変更可能です。
(電源をONしているUSBコネクタ名が、時計画面時 LCD右上に表示されます。)
また、5つ全ての電源をONし終わると、「Span」で設定された時間(初期値 7日)が経過するまで待ち 待ち時間終了後は USB電源のON動作に戻ります。
●画面一覧
ロータリーエンコーダを回すと、以下の画面に切り替えることが出来ます。
No. | 画面 | 操作 |
---|---|---|
1 | 時計 | ロータリーエンコーダの長押しで時計設定 |
2 | USB1 | ロータリーエンコーダの長押しで ON時間設定 |
・ | ||
6 | USB5 | ロータリーエンコーダの長押しで ON時間設定 |
7 | Span | ロータリーエンコーダの長押しで Span設定 |
●I/O一覧
Port B | Port C | Port D | |
---|---|---|---|
bit7 | USB5電源 | LCD_RS | |
bit6 | USB4電源 | LCD_E | |
bit5 | エンコーダ LED_R | SCL (RTC) | LCD_D7 |
bit4 | エンコーダ LED_G | SDA (RTC) | LCD_D6 |
bit3 | エンコーダ LED_B | エンコーダB相 | LCD_D5 |
bit2 | USB3電源 | エンコーダA相 | LCD_D4 |
bit1 | USB2電源 | エンコーダSW | TxD |
bit0 | USB1電源 | RTC_1s | RxD |
USB充電タイマのソース
#include <LiquidCrystal.h>
#include <Wire.h>
#include "RX8900.h"
// I/O定義
#define LED_R 13
#define LED_G 12
#define LED_B 11
#define RTC_INT 14
#define dRTC_1s 0x01 // (0000 000x)
#define dKey_sw 0x02 // (0000 00x0)
#define ENC_SW 15
#define ENC_A 16
#define ENC_B 17
#define EncMsk 0x0c // (0000 xx00)
#define EncMskA 0x04 // (0000 0x00)
#define EncMskB 0x08 // (0000 x000)
#define dUSB1_power 0x01
#define dUSB2_power 0x02
#define dUSB3_power 0x04
#define dUSB4_power 0x40
#define dUSB5_power 0x80
// (kcmd)
#define k_sw 1 // push-sw
#define k_enM 2 // enc Minus
#define k_enP 3 // enc Plus
// (SpanTime)
#define maxSpanTime 14 // max 14日
// (onTime)
#define maxOnTime 12 // max 12h
// initialize the library by associating any needed LCD interface pin
// with the arduino pin number it is connected to
const int rs = 7, en = 6, d4 = 2, d5 = 3, d6 = 4, d7 = 5;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
// ***
long blink_tim0, rtc_tim0, sw_tim0, cmd_tim0;
long time_cnt=0, USB_sec0=0, USB_sec1=0;
char dLED = 0;
uint8_t sw[10]; // sw[0] .. sw[9]
uint8_t sw_bak, sw_rise=0, fRise=0, kcmd=0;
uint8_t fEnc=0, enc_cnt;
char enc_dir=0;
dateTime tim, tim2;
uint8_t temp, fRTC, sec_bak;
dateTime date = {0, 35, 22, SUN, 11, 04, 21}; //初期値-秒-分-時-曜日-日-月-年
uint8_t item_num = 0;
uint8_t cmd_num = 0;
uint8_t work_item = 0;
uint8_t USB_task=0;
long SpanTime = 7; // 初期値 7日
int onTime[6] = {6,6,6,6,6,6}; // 初期値 6h
//■ *****
void setup() {
// put your setup code here, to run once:
DDRB = 0xff; // (oooo oooo):"1"で出力
PORTB = 0x00; // (0000 0000):ポートB
pinMode(RTC_INT, INPUT_PULLUP); // PC0(D14):
pinMode(ENC_SW, INPUT); // PC1(D15):
pinMode(ENC_A, INPUT); // PC2(D16):
pinMode(ENC_B, INPUT); // PC3(D17):
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
lcd.clear();
delay(3);
lcd.print("RTC: RX8900");
// RX8900(RTC)
RX8900.begin(&date); //初期値を指定する場合
sec_bak = tim.second;
blink_tim0 = 10;
rtc_tim0 = 20;
sw_tim0 = 30;
sw[0] = PINC;
sw[1] = PINC;
sw[2] = PINC;
sw[3] = PINC;
}
void loop() {
// put your main code here, to run repeatedly:
get_sw(); // sw[0] = PINC & (0000 xxx0)
// sw_bak = old sw[0]
// sw_rise
count_1s(); // time_cnt++, (fRTC) = 0xff ※RTCの1sパルスをカウント
chk_enc(); // inc/dec (enc_cnt), (fEnc) = 0xff, (enc_dir) = +1/-1
chk_key(); // push-SW check
cmd_ctrl(); //
USB_power(); //
}
// ■
void USB_power() {
switch (USB_task) {
case 0: // USB1 Power ON
USB_sec0 = time_cnt; //(USB_sec0):Span基準時間 = 現在の時間(s)
USB_sec1 = time_cnt; //(USB_sec1):電源ON基準時間 = 現在の時間(s)
PORTB |= dUSB1_power; // USB1 電源ON
USB_task++;
break;
case 1: // USB1 power OFF待ち
if ( (time_cnt - USB_sec1) >= (onTime[1] *3600) ) {
PORTB &= ~dUSB1_power; // USB1 電源OFF
USB_task++;
}
break;
case 2: // USB2 Power ON
USB_sec1 = time_cnt; //(USB_sec1):電源ON基準時間 = 現在の時間(s)
PORTB |= dUSB2_power; // USB2 電源ON
USB_task++;
break;
case 3: // USB2 power OFF待ち
if ( (time_cnt - USB_sec1) >= (onTime[2] *3600) ) {
PORTB &= ~dUSB2_power; // USB2 電源OFF
USB_task++;
}
break;
case 4: // USB3 Power ON
USB_sec1 = time_cnt; //(USB_sec1):電源ON基準時間 = 現在の時間(s)
PORTB |= dUSB3_power; // USB3 電源ON
USB_task++;
break;
case 5: // USB3 power OFF待ち
if ( (time_cnt - USB_sec1) >= (onTime[3] *3600) ) {
PORTB &= ~dUSB3_power; // USB3 電源OFF
USB_task++;
}
break;
case 6: // USB4 Power ON
USB_sec1 = time_cnt; //(USB_sec1):電源ON基準時間 = 現在の時間(s)
PORTB |= dUSB4_power; // USB4 電源ON
USB_task++;
break;
case 7: // USB4 power OFF待ち
if ( (time_cnt - USB_sec1) >= (onTime[4] *3600) ) {
PORTB &= ~dUSB4_power; // USB4 電源OFF
USB_task++;
}
break;
case 8: // USB5 Power ON
USB_sec1 = time_cnt; //(USB_sec1):電源ON基準時間 = 現在の時間(s)
PORTB |= dUSB5_power; // USB5 電源ON
USB_task++;
break;
case 9: // USB5 power OFF待ち
if ( (time_cnt - USB_sec1) >= (onTime[5] *3600) ) {
PORTB &= ~dUSB5_power; // USB5 電源OFF
USB_task++;
}
break;
case 10: // Span周期 完了待ち
if ( (time_cnt - USB_sec0) >= (SpanTime *3600*24) ) {
USB_task = 0;
}
break;
default:
USB_task = 0;
break;
}
}
// ■
void cmd_ctrl() {
switch (cmd_num) {
case 0: //■ 初期設定 (cold)
lcd.clear();
delay(3);
cmd_num++;
break;
case 1: // 初期設定-2 (hot)
cmd_num++;
break;
case 2: //■ キー・チェック ・・・時計表示/USB ch表示
kw02();
disp_cmd1();
break;
case 5: // 長押しチェック ・・・長押しで時計設定へ
kw05();
break;
//■ 時計 設定
case 10: // 年
tim2 = tim;
tim2.second = 0;
lcd.clear();
delay(3);
lcd.setCursor(2, 0);
lcd.print("Year:"); // 5
print_Year(&tim2); // 4
cmd_num++;
break;
case 11: //
kw10();
break;
case 12: // 月
lcd.setCursor(2, 0);
lcd.print("Month: "); // 7
print_Month(&tim2); // 2
cmd_num++;
break;
case 13: //
kw12();
break;
case 14: // 日
lcd.setCursor(2, 0);
lcd.print("Day: "); // 7
print_Day(&tim2); // 2
cmd_num++;
break;
case 15: //
kw14();
break;
case 16: // 時
lcd.setCursor(2, 0);
lcd.print("Hour: "); // 7
print_Hour(&tim2); // 2
cmd_num++;
break;
case 17: //
kw16();
break;
case 18: // 分
lcd.setCursor(2, 0);
lcd.print("Min: "); // 7
print_Min(&tim2); // 2
cmd_num++;
break;
case 19: //
kw18();
break;
case 20: // 秒
lcd.setCursor(2, 0);
lcd.print("Sec: "); // 7
print_Sec(&tim2); // 2
cmd_num++;
break;
case 21: //
kw20();
break;
//■ USB電源出力 設定
case 30: //
lcd.clear();
delay(3);
// 1行目
lcd.setCursor(0, 0);
lcd.print("USB"); // 3
lcd.print(item_num); // 1
lcd.print(":"); // 1
// 2行目
lcd.setCursor(2, 1); // 2
lcd.print("Span: "); // 6
print_Span(item_num); // 2
lcd.print("day"); // 3
cmd_num++;
break;
case 31: // キー・チェック
kw32(); // Span操作
break;
case 32: // ON time 表示
// 1行目
lcd.setCursor(3, 0); // 3
lcd.print(item_num); // 1
lcd.setCursor(2, 1); // 2
lcd.print("ON: "); // 6
print_onTime(item_num); // 2
lcd.print("h "); // 4
cmd_num++;
break;
case 33: // キー・チェック
kw33(); // ON time操作
break;
case 34: // 長押しチェック
kw34();
break;
default:
cmd_num = 0;
break;
}
}
void kw02() {
switch (kcmd) {
case 1: // Push
kcmd = 0;
cmd_tim0 = millis();
cmd_num=5;
break;
case 2: // Minus
kcmd = 0;
if (item_num > 0) { item_num --; }
disp_cmd1a();
break;
case 3: // Plus
kcmd = 0;
if (item_num < 6) { item_num++; }
disp_cmd1a();
break;
default:
kcmd = 0;
break;
}
}
void kw05() {
if ( (sw[0] & 0x02) != 0) { // キー押しで"1"
if ( (millis()-cmd_tim0) > 3000) {
switch (item_num) {
case 0: // time
cmd_num = 10;
break;
case 1: // USB1
case 2: // USB2
case 3: // USB3
case 4: // USB4
case 5: // USB5
cmd_num = 32;
break;
case 6: // SPAN設定
cmd_num = 30;
break;
default:
break;
}
}
} else {
cmd_num = 0;
disp_cmd1a();
}
}
// 10: 日付/時間の変更
void kw10() {
switch (kcmd) {
case 1: // Push
kcmd = 0;
cmd_num ++;
break;
case 2: // Minus
kcmd = 0;
tim2.year--;
lcd.setCursor(7, 0);
print_Year(&tim2);
break;
case 3: // Plus
kcmd = 0;
tim2.year++;
lcd.setCursor(7, 0);
print_Year(&tim2);
break;
default:
kcmd = 0;
break;
}
}
void kw12() {
switch (kcmd) {
case 1: // Push
kcmd = 0;
cmd_num ++;
break;
case 2: // Minus
kcmd = 0;
tim2.month--;
lcd.setCursor(9, 0);
print_Month(&tim2);
break;
case 3: // Plus
kcmd = 0;
tim2.month++;
lcd.setCursor(9, 0);
print_Month(&tim2);
break;
default:
kcmd = 0;
break;
}
}
void kw14() {
switch (kcmd) {
case 1: // Push
kcmd = 0;
cmd_num ++;
break;
case 2: // Minus
kcmd = 0;
tim2.day--;
lcd.setCursor(9, 0);
print_Day(&tim2);
break;
case 3: // Plus
kcmd = 0;
tim2.day++;
lcd.setCursor(9, 0);
print_Day(&tim2);
break;
default:
kcmd = 0;
break;
}
}
void kw16() {
switch (kcmd) {
case 1: // Push
kcmd = 0;
cmd_num ++;
break;
case 2: // Minus
kcmd = 0;
tim2.hour--;
lcd.setCursor(9, 0);
print_Hour(&tim2);
break;
case 3: // Plus
kcmd = 0;
tim2.hour++;
lcd.setCursor(9, 0);
print_Hour(&tim2);
break;
default:
kcmd = 0;
break;
}
}
void kw18() {
switch (kcmd) {
case 1: // Push
kcmd = 0;
cmd_num ++;
break;
case 2: // Minus
kcmd = 0;
tim2.minute--;
lcd.setCursor(9, 0);
print_Min(&tim2);
break;
case 3: // Plus
kcmd = 0;
tim2.minute++;
lcd.setCursor(9, 0);
print_Min(&tim2);
break;
default:
kcmd = 0;
break;
}
}
void kw20() {
switch (kcmd) {
case 1: // Push
kcmd = 0;
RX8900.setDateTime(&tim2); // write RTC
cmd_num ++;
break;
case 2: // Minus
kcmd = 0;
tim2.second--;
lcd.setCursor(9, 0);
print_Sec(&tim2);
break;
case 3: // Plus
kcmd = 0;
tim2.second++;
lcd.setCursor(9, 0);
print_Sec(&tim2);
break;
default:
kcmd = 0;
break;
}
}
void kw22() {
cmd_num =0;
}
void kw32() { // Span設定
switch (kcmd) {
case 1: // Push
kcmd = 0;
cmd_num = 0;
break;
case 2: // Minus
kcmd = 0;
Span_Minus(item_num);
cmd_num--; // 変更値を表示
break;
case 3: // Plus
kcmd = 0;
Span_Plus(item_num);
cmd_num--; // 変更値を表示
break;
default:
kcmd = 0;
break;
}
}
void kw33() { // ON時間設定
switch (kcmd) {
case 1: // Push
kcmd = 0;
cmd_tim0 = millis();
// cmd_num++; // 長押しチェックへ
cmd_num = 0;
break;
case 2: // Minus
kcmd = 0;
onTim_Minus(item_num);
cmd_num--; // 変更値を表示
break;
case 3: // Plus
kcmd = 0;
onTim_Plus(item_num);
cmd_num--; // 変更値を表示
break;
default:
kcmd = 0;
break;
}
}
void kw34() { // 長押しチェック
if ( (sw[0] & 0x02) != 0) { // キー押しで"1"
if ( (millis()-cmd_tim0) > 3000) {
cmd_num = 0;
}
} else { cmd_num--; } // キーOFFは ON時間設定に戻る
}
void cal_enc_u8(uint8_t *cnt) {
if ( (fEnc & 0x01) != 0) {
fEnc &= 0xfe; // (xxxx xxx0)
*cnt += enc_dir;
}
}
// ■■ Disp ■■
void disp_ctrl() {
}
void disp_cmd1() {
if ( (fRTC & 0x01) != 0) {
fRTC &= 0xfe; // (xxxx xxx0)
disp_cmd1a();
}
}
void disp_cmd1a() {
switch (item_num) {
case 0:
lcd.setCursor(0, 0);
print_date(&tim);
lcd.setCursor(12, 0);
switch (USB_task) { //
case 0:
case 1:
lcd.print("U1");
break;
case 2:
case 3:
lcd.print("U2");
break;
case 4:
case 5:
lcd.print("U3");
break;
case 6:
case 7:
lcd.print("U4");
break;
case 8:
case 9:
lcd.print("U5");
break;
default:
lcd.print("--");
break;
}
lcd.setCursor(1, 1);
print_time(&tim);
lcd.print(" "); // 1
print_temp(); // 6
break;
// USB ON Time設定
case 1:
case 2:
case 3:
case 4:
case 5:
lcd.setCursor(0, 0);
lcd.print("USB"); // 3
lcd.print(item_num); // 2
lcd.print(": "); //12
lcd.setCursor(0, 1); // 0
lcd.print(" ON: "); // 5
print_onTime(item_num); // 2
lcd.print("h ");// 10
break;
case 6: // Span設定
lcd.setCursor(0, 0);
lcd.print("Span: ");
lcd.setCursor(0, 1);
lcd.print(" Span: "); // 7
print_Span(item_num); // 2
lcd.print("day "); // 8
break;
default:
lcd.setCursor(0, 0);
lcd.print("----:");
lcd.setCursor(0, 1);
lcd.print(" ");
break;
}
}
// ■■
void Span_Minus(uint8_t item_num){ // SpanTime[] 減算
if (SpanTime > 1) { SpanTime--; }
}
void Span_Plus(uint8_t item_num){ // SpanTime[] 加算
if (SpanTime < maxSpanTime) { SpanTime++; }
}
void onTim_Minus(uint8_t item_num){ // onTime[] 減算
if (onTime[item_num] > 0) { onTime[item_num]--; }
}
void onTim_Plus(uint8_t item_num){ // onTime[] 加算
if (onTime[item_num] < maxOnTime) { onTime[item_num]++; }
}
void count_1s() {
if (fRise == 0x01) {
if ( (sw_rise & dRTC_1s) != 0) { // rise RTC_1s (xxxx xxx?)
time_cnt++;
RX8900.getDateTime(&tim);
fRTC = 0xff;
}
fRise = fRise << 1; // (0000 0010)
}
}
void chk_enc() {
if (fRise == 0x02) {
if ( (sw_rise & EncMskA) != 0) { // rise Phase A (xxxx x?xx)
fEnc = 0xff;
if ( (sw[0] & EncMskB) == 0) {
enc_cnt++ ;
enc_dir = 1;
dLED += 8; // (0000 1000)
kcmd = k_enP;
} else {
enc_cnt-- ;
enc_dir = -1;
dLED -= 8; // (0000 1000)
kcmd = k_enM;
}
dLED &= 0x38; // (00xx x000)
PORTB &= 0xc7; // (xx00 0xxx)
PORTB |= dLED;
}
fRise = fRise << 1; // (0000 0100)
}
}
void chk_key() {
if (fRise == 0x04) {
if ( (sw_rise & dKey_sw) != 0) { // rise RTC_1s (xxxx xx?x)
kcmd = k_sw;
}
fRise = fRise << 1; // (0000 1000)
}
}
void get_sw() {
uint8_t hi_d, lo_d;
if ( (millis()-sw_tim0) > 3) {
sw_tim0 =millis();
sw[3] = sw[2];
sw[2] = sw[1];
sw[1] = PINC;
sw_bak = sw[0];
hi_d = sw[3] & sw[2] & sw[1] & 0x0f; // (0000 xxxx)
lo_d = sw[3] | sw[2] | sw[1] | 0xf0; // (1111 xxxx)
sw[0] |= hi_d;
sw[0] &= lo_d;
sw_rise = (sw_bak ^ sw[0]) & sw[0]; // rise is "1"
if ( (sw_bak ^ sw[0]) != 0) { fRise=0x01; } // change
}
}
void get_rtc() {
if ( (millis()-rtc_tim0) > 100) {
rtc_tim0 = millis();
RX8900.getDateTime(&tim);
if (sec_bak == tim.second) { fRTC = 0xff; }
print_DateTime(&tim);
lcd.setCursor(10, 1);
print_temp();
}
}
// ■■ Print ■■
void print_Span(uint8_t item_num) { // 2
lcd.print(SpanTime);
}
void print_onTime(uint8_t item_num){ // 2
lcd.print(onTime[item_num]);
}
void print_Year(struct dateTime *tim) {
lcd.print("20");
if(tim->year < 10){
lcd.print('0');
}
lcd.print(tim->year);
}
void print_Month(struct dateTime *tim) {
if(tim->month < 10){
lcd.print('0');
}
lcd.print(tim->month);
}
void print_Day(struct dateTime *tim) {
if(tim->day < 10){
lcd.print('0');
}
lcd.print(tim->day);
}
void print_Hour(struct dateTime *tim) {
if(tim->hour < 10){
lcd.print('0');
}
lcd.print(tim->hour);
}
void print_Min(struct dateTime *tim) {
if(tim->minute < 10){
lcd.print('0');
}
lcd.print(tim->minute);
}
void print_Sec(struct dateTime *tim) {
if(tim->second < 10){
lcd.print('0');
}
lcd.print(tim->second);
}
void print_date(struct dateTime *tim) {
print_Year(tim);
lcd.print("/");
print_Month(tim);
lcd.print("/");
print_Day(tim);
//lcd.print(" ");
}
void print_time(struct dateTime *tim) {
print_Hour(tim);
lcd.print(":");
print_Min(tim);
lcd.print(":");
print_Sec(tim);
}
void print_dat_u8(uint8_t dat) {
lcd.print(dat/100);
lcd.print( (dat % 100)/10);
lcd.print(dat % 10);
}
void print_EncDat() {
lcd.print(digitalRead(RTC_INT));
lcd.print(digitalRead(ENC_SW));
lcd.print(digitalRead(ENC_A));
lcd.print(digitalRead(ENC_B));
}
void print_DateTime(struct dateTime *tim) {
lcd.setCursor(0, 0);
print_date(tim);
lcd.setCursor(1, 1);
print_time(tim);
}
void print_temp() {
RX8900.getTemp(&temp);
lcd.print(((float)temp * 2 - 187.19)/ 3.218);
lcd.print('C');
}
}
投稿者の人気記事
-
marua
さんが
2021/11/13
に
編集
をしました。
(メッセージ: 初版)
-
marua
さんが
2021/11/18
に
編集
をしました。
(メッセージ: IO一覧追加/SpanTimeの型変更)
-
marua
さんが
2021/11/20
に
編集
をしました。
(メッセージ: 回路図を追加)
-
marua
さんが
2021/11/22
に
編集
をしました。
(メッセージ: 回路図 訂正)
Opening
3duilab
2021/12/02
marua
2021/12/07
ログインしてコメントを投稿するこれ、需要はあると思います。
タイマーじゃなくて、インピーダンスを測って
だいたい70~90%くらいでオフするとさらに便利です。
コメント ありがとうございます。
良く充電をし忘れてしまうので、つなぎっぱなしでもバッテリーにダメージを与えないよう 作成しました。
USB電源を1つ減らして、アナログ入力で電流値を読むのも良さそうですね。