maruaのアイコン画像

USB 充電タイマ

marua 2021年11月13日に作成  (2021年11月22日に更新)

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のアイコン画像 3duilab 前の木曜日の12:46

    これ、需要はあると思います。
    タイマーじゃなくて、インピーダンスを測って
    だいたい70~90%くらいでオフするとさらに便利です。

    0 件の返信が折りたたまれています
ログインしてコメントを投稿する

投稿者の人気記事