交流100Vからトランス無しで直流12Vぐらいを取り出しますました。FSD210B
 
以前の記事

交流100Vからトランス無しで直流9Vぐらいを取り出しますました。FSD210B
におきまして、
 
以前の記事抜粋。
 『Voutに100Ωの抵抗を負荷として接続して
  7.95Vで80mA出力できていますので
  秋月ページで書いてある「12Vで100mA」に
  まあまあ近いような気がする数字です』
とか言っていますので、
『12Vで100mAに挑戦。』という。みたいな記事です。
いちおう、12Vのツェナーダイオードを準備しまして。
予定通り
100Ωの抵抗をつなぐと、
 

 
7.6ボルトで78.8ミリアンペア
12ボルトとか出てないしねーみたいな結果に。
予定とぜんぜん違うので、びっくりみたいな結果に。
で、
LED一つから一つづつ増やしてみました。みたいな。テスト。
■無負荷
 

12.6V
 
■LED1個

12.2ボルト、23.8ミリアンペア
 
■LED2個

12.1ボルト、47.6ミリアンペア
 
■LED3個
 

 
11.9ボルト、69.1アンペア
ここで12ボルトを切ってしまう。
■LED4個

8.7ボルト、74.4アンペア
もう駄目っぽい。
って事で、正しい作り方から逸れているかもしれない、と思いますけど、
いちおう、この回路でまー50mAぐらいまでなら安定して取り出せそうです。
5Vまで落とせばこれの倍ぐらいまでは取れると思いますので
arduino一つと送信機一つみたいなので許してください。
 

メタンガスセンサーのMQ-4
 
メタンガスセンサー MQ-4を秋月から買ってきた。
データシートを見るとヒーターの回路と、センサーの回路に分かれていて、
メタンガスを検知すると内部抵抗が変化することを利用しを検知する
方式のようです。
 
秋月の解説によると
「メタンに高い感度を持ち、メタンガスを選択的に検出できます。」
MQ-2がガス一般なのに比較してメタン専門って事みたいですね。
 
■データシートの回路
 

 
■データシートのパラメーターとピン配置
 

 
■回路
 

 
4.7KΩのところの電圧を測定することで煙(ガス)を測定します。
 
■電源電圧を5.0Vで抵抗を4.7KΩにした場合のグラフです。
 

 
電圧とppmのテーブルを持つか、近似関数を用意することで
電圧からPPMへ変換ができそうです。
 

 

 

煙センサーのMQ-2
 
ガスセンサー(煙センサー) MQ-2を秋月から買ってきた。
 
データシートを見るとヒーターの回路と、センサーの回路に分かれていて、
煙(ガス)を検知すると内部抵抗が変化することを利用しを検知する
方式のようです。
 
秋月の解説によると
「プロパンや煙に高い感度を持ちます。また、天然ガス、その他の可燃性ガスにも対応しています。」
って事なので、いわゆるガス一般って所なんでしょうかね?
といいますか、このMQシリーズ、回路も仕組みも同じみたいですね。
前のMQ-3Bと回路も測定範囲も同じです。
 
■データシートの回路
 

 
■データシートのパラメーターとピン配置
 

 
■回路
 

 
4.7KΩのところの電圧を測定することで煙(ガス)を測定します。
 
■電源電圧を5.0Vで抵抗を4.7KΩにした場合のグラフです。
 

 
電圧とppmのテーブルを持つか、近似関数を用意することで
電圧からPPMへ変換ができそうです。
 

 

 

アルコールセンサーのMQ-3B
 
アルコールセンサーのMQ-3Bが秋月にあったので買ってきてみました。
データシートを見るとヒーターの回路と、センサーの回路に分かれていて、
アルコールを検知すると内部抵抗が変化することを利用してアルコールを検知する
方式のようです。
 
■データシートの回路
 

 
■データシートのパラメーターとピン配置
 

 
■回路
 

 
4.7KΩのところの電圧を測定することでアルコール濃度を測定します。
 
■電源電圧を5.0Vで抵抗を4.7KΩにした場合のグラフです。
 

 
電圧とppmのテーブルを持つか、近似関数を用意することで
電圧からPPMへ変換ができそうです。
 
■実験風景
 
https://youtu.be/jV_IvLpHwsw


 

自動水やりシステム
 
前の記事
 https://ameblo.jp/fc2miha/entry-12833772963.html

とか、その前の記事
 https://ameblo.jp/fc2miha/entry-12833772945.html

 

それと、前の記事もその前の記事もプログラムが間違っておりまして、
50日でおかしくなりました。

 

修正しました。今回こそ大丈夫です。(\(^o^)/)

 

■プログラム

#include <stdio.h>
#include <time.h>

#include <LiquidCrystal.h>
//LCD        arduino
// 4(rs)     D12
// 5(rw)     D11
// 6(enable) D10 
// 11(db4)    D5
// 12(db5)    D4
// 13(db6)    D3
// 14(db7)    D2
LiquidCrystal lcd(12, 11, 10, 5, 4, 3, 2);

//
//D8をSSRへ接続
//

struct BUTTON
{
  int value;
  int flag;
  int input_pin;
};
struct BUTTON bt_select;
struct BUTTON bt_enter;
int lcd_back_light = 1;

unsigned long system_millis = 0;
unsigned long old_millis = 0;
int overflow = 0;

unsigned long miha_millis(int flag)
{
  if(flag==1){
    // 
    system_millis = millis();
    //
    //debug start
    //system_millis = system_millis + (10000000/12/24*10);
    //debug ebd
  
    if(system_millis<old_millis){
      Serial.println("********");
      Serial.println("OVERFLOW");
      Serial.println("********");
      overflow = 1;
    }
    old_millis = system_millis;
  }
  
  return system_millis;
}

void button_initialize()
{
  memset(&bt_select, NULL, sizeof(bt_select));
  bt_select.input_pin = 6;
  memset(&bt_enter, NULL, sizeof(bt_enter));
  bt_enter.input_pin = 7;
}

void tick_button(struct BUTTON *bt)
{
  int value = digitalRead(bt->input_pin);
  if(value!=bt->value){
    bt->value = value;
    bt->flag = 1;
//    if(value==HIGH){
//      digitalWrite(13, HIGH);
//      lcd_back_light = 1;
//    }
    delay(10);
  }
}

int get_button(int *status, struct BUTTON *bt)
{
  if(bt->flag == 1){
    *status = bt->value;
    bt->flag = 0;
    return 1;
  }
  else{
    return 0;
  }
}

void setup()
{
  Serial.begin(9600); 
  
  delay(620);

  Serial.print("AcPowerControler version0.3 ¥n");
  
  //
  button_initialize();
  alarm_initialise();

  //lcd back light
  digitalWrite(13, HIGH);

  //SSR
  pinMode(8, OUTPUT);
  digitalWrite(8, LOW);
  //Input
  pinMode(6, INPUT);  //enter button
  pinMode(7, INPUT);  //select button
  //back light
  pinMode(13, OUTPUT);  //LCD back light

  //LCD
  lcd.begin(2,16);
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("AcPowerControler");
  delay(500);
  //
  setup_time_input();
}

void setup_time_input()
{
  int YYYY = input("input year?", 4, 2020, 2020, 2100);
  int MM = input("input month?", 2, 1, 1, 12);
  int DD = input("input day?", 2, 1, 1, 31);
  int hh = input("input hour?", 2, 0, 0, 23);
  int mm = input("input minits?", 2, 0, 0, 59);

  initDateTime(YYYY, MM, DD, hh, mm);

  lcd.clear();

}

int input(char *str, int keta, int def, int mMin, int nMax)
{
  lcd.clear();
  lcd.print(str);
  Serial.print(str);

  int  i = 0;
  char buf[17];
  char ch[2];
  //
  lcd.blink();
  //
  int value = def;

  int flag = 1;
  int status;
  while(1){
    tick_button(&bt_select);
    tick_button(&bt_enter);

    if(flag==1){
      //
      if(keta==4){
        sprintf(buf, "%04d", value);
      }
      if(keta==2){
        sprintf(buf, "%02d", value);
      }
      if(keta==1){
        sprintf(buf, "%01d", value);
      }
      lcd.setCursor(0, 1);
      lcd.print(buf);
      Serial.print(buf);
      lcd.setCursor(keta-1, 1);
      flag = 0;
    }
    if(get_button(&status, &bt_enter)){
      if(status==HIGH){
        return value;
      }
    }
    if(get_button(&status, &bt_select)){
      if(status==HIGH){
        flag = 1;
        value++;
        if(value>nMax){
          value = mMin;
        }
      }
    }
  }
}

void lcd_print(int x, int y, char *str)
{
  lcd.setCursor(x, y);
  lcd.print(str);
  Serial.print(str);
}

struct DateTime {
  time_t base_time;
  unsigned long BaseMillis;
  unsigned long base_startup_keika_sec;
};
struct DateTime date_time;


void initDateTime(int YYYY, int MM, int DD, int hh, int mm)
{
    struct tm now;
    now.tm_year = YYYY-1900;     /* 2001年 */
    now.tm_mon  = MM-1;       /* 5月    */
    now.tm_mday = DD;      /* 20日   */
    now.tm_wday = 0;       /* 日曜日 */
    now.tm_hour = hh;      /* 14時   */
    now.tm_min  = mm;      /* 20分   */
    now.tm_sec  = 0;       /* 00秒   */
    now.tm_isdst= -1;      /* 夏時間無効 */
    
    date_time.base_time = mktime(&now);
    if (date_time.base_time == (time_t) - 1) { //error
      date_time.base_time = 0;
    }
    date_time.BaseMillis = miha_millis(1);
    date_time.base_startup_keika_sec = date_time.BaseMillis / 1000;
}

struct HHMM{
  int HH;
  int MM;
  int YOUBI;
};
struct HHMM hhmm;

unsigned long getSec()
{
  //現在時刻累積秒を求める
  unsigned long now_startup_keika_sec = miha_millis(0) / 1000;
  return date_time.base_time + ((time_t)now_startup_keika_sec - (time_t)date_time.base_startup_keika_sec);
}

void getDateTime(struct HHMM *hhmm)
{
  //現在時刻累積秒を求める
  unsigned long now_startup_keika_sec = miha_millis(0) / 1000;
  time_t now_time = date_time.base_time + ((time_t)now_startup_keika_sec - (time_t)date_time.base_startup_keika_sec);
  
  //localtimeにする
  //now_time = now_time + 60 * 60 * 9;

  struct tm   *time_inf;
  time_inf = localtime(&now_time);

  hhmm->HH = time_inf->tm_hour;
  hhmm->MM = time_inf->tm_min;
  hhmm->YOUBI = time_inf->tm_wday;
}

void getDateTimeStr(char *buf1, char *buf2)
{
  //現在時刻累積秒を求める
  unsigned long now_startup_keika_sec = miha_millis(0) / 1000;
  time_t now_time = date_time.base_time + ((time_t)now_startup_keika_sec - (time_t)date_time.base_startup_keika_sec);
  
  //localtimeにする
  //now_time = now_time + 60 * 60 * 9;

  struct tm   *time_inf;
  time_inf = localtime(&now_time);

  int year = time_inf->tm_year + 1900;
  int month = time_inf->tm_mon + 1;
  int day = time_inf->tm_mday;
  int hour = time_inf->tm_hour;
  int minute = time_inf->tm_min;
  int second = time_inf->tm_sec;
  char *wday[] = {"SUN","MON","TUE","WED","TUE","FRI","SAT"};
  sprintf(buf1, "%04d/%02d/%02d %s", year, month, day, wday[time_inf->tm_wday]);
  sprintf(buf2, "%02d:%02d:%02d", hour, minute, second);
}

int debugCount=0;
void tick_time()
{
  int debugFlag = ((debugCount%100)==0);
  debugCount++;
  if(debugFlag){
    Serial.println("tick_time() start");
  }
  //
  //オーバーフロー前の時刻を取得しておく
  //
  unsigned long now_startup_keika_sec = miha_millis(0) / 1000;
  time_t now_time = date_time.base_time + ((time_t)now_startup_keika_sec - (time_t)date_time.base_startup_keika_sec);
  //
  unsigned long now = miha_millis(1);
  if(debugFlag){
    Serial.print("now=");
    Serial.println(now);
    Serial.print("date_time.BaseMillis=");
    Serial.println(date_time.BaseMillis);
  }
  if(overflow){
    date_time.base_time = now_time; //overflow前の直前の時刻を設定
    date_time.BaseMillis = now;
    date_time.base_startup_keika_sec = date_time.BaseMillis / 1000;
    Serial.println("tick_time() point 1");
    overflow=0;
  }
  if(debugFlag){
    Serial.println("tick_time() end");
  }
}

struct ALARM
{
  int mode;   //  0=week, 1=alway on, 2=alway off, 3=after on, 4=after off
  int SUN;
  int MON;
  int TUE;
  int WED;
  int THU;
  int FRI;
  int SAT;
  int start_hh;
  int start_mm;
  int end_hh;
  int end_mm;
  int after_on_hh;
  int after_off_hh;
  unsigned long setSec;
};
struct ALARM alarm;

int flag = 0;
unsigned long nCount = 0;
void loop()
{
  nCount++;

  //lcd.noCursor();

  tick_time();
  tick_button(&bt_select);
  tick_button(&bt_enter);

  //LCD back light
  if(lcd_back_light==1){
    digitalWrite(13, HIGH);
  }
  else{
    digitalWrite(13, LOW);
  }
  
  //DEBUG
  //int kakeru = 60;    //DEBUG
  int kakeru = 3600;  //本番向け
  //
  unsigned long nowSec = getSec();
  //表示
  if(alarm.mode==0 || alarm.mode==1 || alarm.mode==2){  //week alway
    char date[20];
    char time[20];
    getDateTimeStr(date, time);
    lcd_print(0, 0, date);
    lcd_print(0, 1, time);
    if(alarm.mode==0){
      lcd_print(8, 1, " weekly  ");
    }
    if(alarm.mode==1){
      lcd_print(8, 1, " all ON  ");
    }
    if(alarm.mode==2){
      lcd_print(8, 1, " all OFF ");
    }
  }else
  if(alarm.mode==3 || alarm.mode==4){
    int sec;
    if(alarm.mode==3){
      sec = alarm.setSec+(alarm.after_on_hh * kakeru) - nowSec;
    }
    else{
      sec = alarm.setSec+(alarm.after_off_hh * kakeru) - nowSec;
    }
//    Serial.print("sec=");
//    Serial.println(sec);
    if(sec<0){
      sec=0;
    }
    int hh = sec / 3600;
    int mm = (sec - (hh * 3600))/60;
    int ss = sec % 60;
    char buf[17];
    sprintf(buf, "%02d:%02d:%02d", hh, mm, ss);
    lcd_print(0, 0, buf);
    if(alarm.mode==3){
      lcd_print(8, 1, " afterON ");
    }
    if(alarm.mode==4){
      lcd_print(8, 1, "afterOFF");
    }
  }
  

  lcd.setCursor(1, 0);

  //現在時刻取得
  struct HHMM hhmm;
  getDateTime(&hhmm);
  //日付跨ぎの補正
  int alarmStartHHMM = (alarm.start_hh * 100 + alarm.start_mm)  ;
  int alarmEndHHMM   = (alarm.end_hh * 100 + alarm.end_mm);
  int nowHHMM = (hhmm.HH * 100 + hhmm.MM);
  if(alarmStartHHMM>alarmEndHHMM){
    alarmEndHHMM = alarmEndHHMM + 2400;
  }
  if(alarmStartHHMM>nowHHMM){
    nowHHMM = nowHHMM + 2400;
  }
  //
  if(alarm.mode==0){  //week
    //
    if((hhmm.YOUBI==0 && alarm.SUN==1)
     ||(hhmm.YOUBI==1 && alarm.MON==1)
     ||(hhmm.YOUBI==2 && alarm.TUE==1)
     ||(hhmm.YOUBI==3 && alarm.WED==1)
     ||(hhmm.YOUBI==4 && alarm.THU==1)
     ||(hhmm.YOUBI==5 && alarm.FRI==1)
     ||(hhmm.YOUBI==6 && alarm.SAT==1)){
  /*    
   *   if( (alarm.start_hh * 100 + alarm.start_mm) <= (hhmm.HH * 100 + hhmm.MM) 
                                                  && (hhmm.HH * 100 + hhmm.MM) < (alarm.end_hh * 100 + alarm.end_mm)){
  */
      if( alarmStartHHMM <= nowHHMM && nowHHMM < alarmEndHHMM){
        digitalWrite(8, HIGH);
      }
      else{
        digitalWrite(8, LOW);
      }
    }
    else{
      digitalWrite(8, LOW);
    }
  }else
  if(alarm.mode==1){    //alway on
    digitalWrite(8, HIGH);
  }else
  if(alarm.mode==2){    //alway off
    digitalWrite(8, LOW);
  }else
  if(alarm.mode==3){    //after on
    if(alarm.setSec+(alarm.after_on_hh * kakeru) >= nowSec){
      digitalWrite(8, LOW);
    }
    else{
      digitalWrite(8, HIGH);
    }
  }else
  if(alarm.mode==4){    //after off
    if(alarm.setSec+(alarm.after_off_hh * kakeru) <= nowSec){
      digitalWrite(8, LOW);
    }
    else{
      digitalWrite(8, HIGH);
    }
  }
  
  
  //
  int status;
  if(get_button(&status, &bt_select)){
    if(status==HIGH){
      //alarm_input();
      if(lcd_back_light==0){
        lcd_back_light = 1;
      }
      else{
        lcd_back_light = 0;
      }
    }
  }
  if(get_button(&status, &bt_enter)){
    if(status==HIGH){
      alarm_input();
    }
  }
  delay(1);
}

void alarm_initialise()
{
  memset(&alarm, NULL, sizeof(struct ALARM));
  alarm.SUN = 1;
  alarm.MON = 1;
  alarm.TUE = 1;
  alarm.WED = 1;
  alarm.THU = 1;
  alarm.FRI = 1;
  alarm.SAT = 1;
  alarm.after_on_hh = 1;
  alarm.after_off_hh = 1;
}

void alarm_input()
{
  //lcd back light
  digitalWrite(13, HIGH);
  //
  alarm.mode = select_input5("mode :", "weekly    ", "always on ", "always off", "after on  ", "after off ", alarm.mode);
  if(alarm.mode==0){
    //weekly
    alarm.SUN = select_input("[SUN] : ", "  ", "ON", alarm.SUN);
    alarm.MON = select_input("[MON] : ", "  ", "ON", alarm.MON);
    alarm.TUE = select_input("[TUE] : ", "  ", "ON", alarm.TUE);
    alarm.WED = select_input("[WED] : ", "  ", "ON", alarm.WED);
    alarm.THU = select_input("[THU] : ", "  ", "ON", alarm.THU);
    alarm.FRI = select_input("[FRI] : ", "  ", "ON", alarm.FRI);
    alarm.SAT = select_input("[SAT] : ", "  ", "ON", alarm.SAT);
  
    alarm.start_hh = input("start hour",   2, alarm.start_hh, 0, 23);
    alarm.start_mm = input("start minits", 2, alarm.start_mm, 0, 59);
    alarm.end_hh   = input("end hour",     2, alarm.end_hh,   0, 23);
    alarm.end_mm   = input("end minits",   2, alarm.end_mm,   0, 59);
  }
  if(alarm.mode==3){
    //after on
    alarm.after_on_hh = input("after hour",   1, alarm.after_on_hh, 1, 9);
    alarm.setSec = getSec();
    Serial.print("alarm.setSec=");
    Serial.println(alarm.setSec);
  }
  if(alarm.mode==4){
    //after off
    alarm.after_off_hh = input("after hour",   1, alarm.after_off_hh, 1, 9);
    alarm.setSec = getSec();
  }

  lcd.clear();

  lcd_back_light = 1;
}

int select_input(char *str, char *select1, char *select2, int def)
{
  lcd.clear();
  lcd.print(str);
  Serial.print(str);

  int  value = def;
  char buf[17];
  char select[17];
  //
  lcd.blink();
  //
  strcpy(buf, "");
  strcpy(select, select1);

  int flag = 1;
  int status;
  while(1){
    tick_button(&bt_select);
    tick_button(&bt_enter);

    if(flag==1){
      if(value==0){
        sprintf(buf, "%s", select1);
      }
      else{
        sprintf(buf, "%s", select2);
      }
      lcd.setCursor(0, 1);
      lcd.print(buf);
      Serial.print(buf);
      lcd.setCursor(strlen(buf)-1, 1);
      flag = 0;
    }
    if(get_button(&status, &bt_enter)){
      if(status==HIGH){
        return value;
      }
    }
    if(get_button(&status, &bt_select)){
      if(status==HIGH){
        flag = 1;
        if(value==0){
          value = 1;
        }
        else{
          value = 0;
        }
      }
    }
  }
  return 0;
}

int select_input5(char *str, char *select1, char *select2, char *select3, char *select4, char *select5, int def)
{
  lcd.clear();
  lcd.print(str);
  Serial.print(str);

  int  value = def;
  char buf[17];
  char select[17];
  //
  lcd.blink();
  //
  int flag = 1;
  int status;
  while(1){
    tick_button(&bt_select);
    tick_button(&bt_enter);

    if(flag==1){
      if(value==0){
        sprintf(buf, "%s", select1);
      }else
      if(value==1){
        sprintf(buf, "%s", select2);
      }else
      if(value==2){
        sprintf(buf, "%s", select3);
      }else
      if(value==3){
        sprintf(buf, "%s", select4);
      }
      else{
        sprintf(buf, "%s", select5);
      }
      lcd.setCursor(0, 1);
      lcd.print(buf);
      Serial.print(buf);
      lcd.setCursor(strlen(buf)-1, 1);
      flag = 0;
    }
    if(get_button(&status, &bt_enter)){
      if(status==HIGH){
        return value;
      }
    }
    if(get_button(&status, &bt_select)){
      if(status==HIGH){
        flag = 1;
        if(value==0){
          value = 1;
        }else
        if(value==1){
          value = 2;
        }else
        if(value==2){
          value = 3;
        }else
        if(value==3){
          value = 4;
        }
        else{
          value = 0;
        }
      }
    }
  }
  return 0;
}

 

RTC-8564NBでうるう年の実験。
前に作った、タイマーなんですが、

やっぱ、50日に一回具合悪くなるので、リアルタイムクロック化して安定させるべく、
以前購入していた、秋月のRTC-8564NBの動作確認テストの記事です。
 
■回路
 

 
■テスト
 
ちゃんと明日になるかな?
ver0.1
  2021  4/17 23:59:55
  2021  4/17 23:59:56
  2021  4/17 23:59:57
  2021  4/17 23:59:58
  2021  4/17 23:59:59
  2021  4/18  0:00:00 ← ちゃんと明日になった。
  2021  4/18  0:00:01
  2021  4/18  0:00:02
  2021  4/18  0:00:03
  2021  4/18  0:00:04
  2021  4/18  0:00:05
■うるう年の確認
    国立天文台の定義によると
    「グレゴリオ暦法では、うるう年を次のように決めています。
    (1)西暦年号が4で割り切れる年をうるう年とする。
    (2)(1)の例外として、西暦年号が100で割り切れて    400で割り切れない年は平年とする。」
    さらに
    「例えば、西暦2004年、2008年、2012年……は(1)に当てはまりますので、
    うるう年になります。また、西暦2100年、2200年、2300年は(2)に当てはまりますので、
    平年となります。さらに、西暦2000年、2400年は、100でも割り切れますが400でも
    割り切れてしまいますので、(2)には当てはまらず、(1)のとおりにうるう年となります。」
    
つまり、直近だと
2020年はうるう年、2024年もうるう年
2000年は100でも400でも割り切れるのでうるう年
かなり未来の2100年は4で割り切れるけど100でも割り切れるのでうるう年じゃない
ってなりますけど。
さて、試してみましょう。
 
今年。2021年2月
ver0.1
  2021  2/28 23:59:55
  2021  2/28 23:59:56
  2021  2/28 23:59:57
  2021  2/28 23:59:58
  2021  2/28 23:59:59 ←うるう年でない。
  2021  3/ 1  0:00:00
  2021  3/ 1  0:00:01
  2021  3/ 1  0:00:02
2020年2月
ver0.1
  2020  2/28 23:59:55
  2020  2/28 23:59:56
  2020  2/28 23:59:57
  2020  2/28 23:59:58
  2020  2/28 23:59:59
  2020  2/29  0:00:00 ←うるう年。
  2020  2/29  0:00:01
  2020  2/29  0:00:02
  2020  2/29  0:00:03
  2020  2/29  0:00:04
2024年2月
ver0.1
  2024  2/28 23:59:55
  2024  2/28 23:59:56
  2024  2/28 23:59:57
  2024  2/28 23:59:58
  2024  2/28 23:59:59
  2024  2/29  0:00:00 ←うるう年。
  2024  2/29  0:00:01
  2024  2/29  0:00:02
  2024  2/29  0:00:03
  2024  2/29  0:00:04
2000年2月
ver0.1
  2000  2/28 23:59:55
  2000  2/28 23:59:56
  2000  2/28 23:59:57
  2000  2/28 23:59:58
  2000  2/28 23:59:59
  2000  2/29  0:00:00 ←うるう年。
  2000  2/29  0:00:01
  2000  2/29  0:00:02
  2000  2/29  0:00:03
  2000  2/29  0:00:04
  2000  2/29  0:00:05
2100年2月
ver0.1
  2000  2/28 23:59:55    ←年がばぐってるね。
  2000  2/28 23:59:56
  2000  2/28 23:59:57
  2000  2/28 23:59:58
  2000  2/28 23:59:59
  2000  2/29  0:00:00    ←うるう年になっているけど。不正解!!
  2000  2/29  0:00:01
  2000  2/29  0:00:02
  2000  2/29  0:00:03
  
これって、2099年の次は2000年に戻るっていうやつだね。
まさこれは2000年問題!!
懐かしい。
そうそう。1999年の年末は世間が浮かれている中
我々はデータセンターで年越ししたものです。
 
やっぱ、2100年問題とかなるのかね?
いちおう、試してみよう。2100年問題。
 
2099年年末
ver0.1
  2099 12/31 23:59:55
  2099 12/31 23:59:56
  2099 12/31 23:59:57
  2099 12/31 23:59:58
  2099 12/31 23:59:59
  2000  1/ 1  0:00:00 ←ちゃんと?2000年に戻っている。
  2000  1/ 1  0:00:01
  2000  1/ 1  0:00:02
  2000  1/ 1  0:00:03
 
このモジュール使う人は2100年問題に気をつけましょう。
原因は年を二桁だけで管理しているためです。
0~99までの範囲をふたつに分けて
 0~50までは2100を加算
 51~99までは2000を加算とすれば問題なくなるのでいちおう、
 2099年まではプログラムを更新して使えるはずですけど
 2100年は「うるう年じゃない」の扱い出来るようにしなきゃですね。
 
※いちおうこのモジュール、センチュリービットなるビットを立てて
 2000年以降を識別できるようですが、2100年以降は識別できるかな?
 ここから先は未来のエンジニアにお任せですね。
 
■プログラム
 
ここらへんhttp://www.straycats.net/wifky/wifky.cgi?p=Arduino+%E3%83%AA%E3%82%A2%E3%83%AB%E3%82%BF%E3%82%A4%E3%83%A0%E3%82%AF%E3%83%AD%E3%83%83%E3%82%AF%E3%80%80RTC-8564NB
を参考にさせてもらいましたが、
コンパイルエラーになるでの、色々変更しました。
ライブラリーもあっちこっち色々あるみたいであれなんで、
一本のプログラムとして合体しちゃいましたので、
ライブラリーの方言?は気にしなくて良くなるはずです。

#include <Arduino.h>
#include <Wire.h>

#define BCD2Decimal(x)  (((x>>4)*10)+(x&0xf))
#define Decimal2BCD(x)          (((x/10)<<4)+(x%10))

extern "C" {
  #include <stdlib.h>
  #include <string.h>
  #include <inttypes.h>
}

#include <inttypes.h>

class RTC8564
{
private:
  void init(void);
  uint8_t _seconds;
  uint8_t _minutes;
  uint8_t _hours;
  uint8_t _days;
  uint8_t _weekdays;
  uint8_t _months;
  uint8_t _years;
  bool  _century;
  
public:
  enum {
    BCD = 0,
    Decimal = 1,
  };
  RTC8564();
  void begin(void);
  void sync(uint8_t date_time[],uint8_t size = 7);
  bool available(void);
  bool isvalid(void);
  uint8_t seconds(uint8_t format = RTC8564::BCD) const;
  uint8_t minutes(uint8_t format = RTC8564::BCD) const;
  uint8_t hours(uint8_t format = RTC8564::BCD) const;
  uint8_t days(uint8_t format = RTC8564::BCD) const;
  uint8_t weekdays() const;
  uint8_t months(uint8_t format = RTC8564::BCD) const;
  uint8_t years(uint8_t format = RTC8564::BCD) const;
  bool century() const;
};

#define RTC8564_SLAVE_ADRS  (0xA2 >> 1)
#define BCD2Decimal(x)    (((x>>4)*10)+(x&0xf))

RTC8564::RTC8564()
  : _seconds(0), _minutes(0), _hours(0), _days(0), _weekdays(0), _months(0), _years(0), _century(0)
{
}

void RTC8564::init(void)
{
  delay(1000);
  Wire.beginTransmission(RTC8564_SLAVE_ADRS);
  Wire.write(0x00);     // write reg addr 00
  Wire.write(0x20);     // 00 Control 1, STOP=1
  Wire.write(0x00);     // 01 Control 2
  Wire.write(0x00);     // 02 Seconds
  Wire.write(0x00);     // 03 Minutes
  Wire.write(0x09);     // 04 Hours
  Wire.write(0x01);     // 05 Days
  Wire.write(0x01);     // 06 Weekdays
  Wire.write(0x01);     // 07 Months
  Wire.write(0x01);     // 08 Years
  Wire.write(0x00);     // 09 Minutes Alarm
  Wire.write(0x00);     // 0A Hours Alarm
  Wire.write(0x00);     // 0B Days Alarm
  Wire.write(0x00);     // 0C Weekdays Alarm
  Wire.write(0x00);     // 0D CLKOUT
  Wire.write(0x00);     // 0E Timer control
  Wire.write(0x00);     // 0F Timer
  Wire.write(0x00);     // 00 Control 1, STOP=0
  Wire.endTransmission();
}

// Public Methods //////////////////////////////////////////////////////////////

void RTC8564::begin(void)
{
  Wire.begin();
  if(isvalid() == false)
    init();
}

void RTC8564::sync(uint8_t date_time[],uint8_t size)
{
  Wire.beginTransmission(RTC8564_SLAVE_ADRS);
  Wire.write(0x00);     // write reg addr 00
  Wire.write(0x20);     // 00 Control 1, STOP=1
  Wire.endTransmission();
  
  Wire.beginTransmission(RTC8564_SLAVE_ADRS);
  Wire.write(0x02);     // write reg addr 02
  Wire.write(date_time, size);
  Wire.endTransmission();
  
  Wire.beginTransmission(RTC8564_SLAVE_ADRS);
  Wire.write(0x00);     // write reg addr 00
  Wire.write(0x00);     // 00 Control 1, STOP=0
  Wire.endTransmission();
}

bool RTC8564::available(void)
{
  uint8_t buff[7];
  
  Wire.beginTransmission(RTC8564_SLAVE_ADRS);
  Wire.write(0x02);     // write reg addr 02
  Wire.endTransmission();
  
  Wire.requestFrom(RTC8564_SLAVE_ADRS, 7);
  
  for(int i=0; i<7; i++){
    if(Wire.available()){
      buff[i] = Wire.read();
    }
  }
  
  _seconds  = buff[0] & 0x7f;
  _minutes  = buff[1] & 0x7f;
  _hours    = buff[2] & 0x3f;
  _days   = buff[3] & 0x3f;
  _weekdays = buff[4] & 0x07;
  _months   = buff[5] & 0x1f;
  _years    = buff[6];
  _century  = (buff[5] & 0x80) ? 1 : 0;
  return (buff[0] & 0x80 ? false : true);
}

bool RTC8564::isvalid(void)
{
  Wire.beginTransmission(RTC8564_SLAVE_ADRS);
  Wire.write(0x02);     // write reg addr 02
  Wire.endTransmission();
  Wire.requestFrom(RTC8564_SLAVE_ADRS, 1);
  if(Wire.available()){
    uint8_t buff = Wire.read();
    return (buff & 0x80 ? false : true);
  }
  return false;
}

uint8_t RTC8564::seconds(uint8_t format) const {
  if(format == Decimal) return BCD2Decimal(_seconds);
  return _seconds;
}

uint8_t RTC8564::minutes(uint8_t format) const {
  if(format == Decimal) return BCD2Decimal(_minutes);
  return _minutes;
}

uint8_t RTC8564::hours(uint8_t format) const {
  if(format == Decimal) return BCD2Decimal(_hours);
  return _hours;
}

uint8_t RTC8564::days(uint8_t format) const {
  if(format == Decimal) return BCD2Decimal(_days);
  return _days;
}

uint8_t RTC8564::weekdays() const {
  return _weekdays;
}

uint8_t RTC8564::months(uint8_t format) const {
  if(format == Decimal) return BCD2Decimal(_months);
  return _months;
}

uint8_t RTC8564::years(uint8_t format) const {
  if(format == Decimal) return BCD2Decimal(_years);
  return _years;
}

bool RTC8564::century() const {
  return _century;
}


// Preinstantiate Objects //////////////////////////////////////////////////////

RTC8564 Rtc = RTC8564();


int yearDecimal2BCD(int yyyy){
 
  int nDec = yyyy -1900;
  int nDecHigh = nDec / 100;
  int nDecLow  = nDec % 100;
  int bcdHigh = Decimal2BCD(nDecHigh);
  int bcdLow  = Decimal2BCD(nDecLow);
  int nBCD = bcdHigh * 256 + bcdLow;

  return nBCD;
}


void setup()
{
  Serial.begin(9600);
  Serial.println("ver0.1");

  Rtc.begin();
  
  //setDateTime(2021, 4, 17, 6, 23, 59, 55);
  //setDateTime(2021, 2, 28, 0, 23, 59, 55);
  //setDateTime(2020, 2, 28, 0, 23, 59, 55);
  //setDateTime(2024, 2, 28, 0, 23, 59, 55);
  //setDateTime(2000, 2, 28, 0, 23, 59, 55);
  //setDateTime(2100, 2, 28, 0, 23, 59, 55);
  setDateTime(2099, 12, 31, 0, 23, 59, 55);
}

void loop()
{
  char buf[256];
  
  Rtc.available();    //取得準備
  sprintf(buf, "  %04d %2d/%2d %2d:%02d:%02d",
      Rtc.years(RTC8564::Decimal)+2000,
      Rtc.months(RTC8564::Decimal),
      Rtc.days(RTC8564::Decimal),
      Rtc.hours(RTC8564::Decimal),
      Rtc.minutes(RTC8564::Decimal),
      Rtc.seconds(RTC8564::Decimal) );
  
  Serial.println(buf);
  
  delay(1000);
      

}

void setDateTime(int yyyy, int mm, int dd, int you, int hour, int min, int sec)
{
 
 
  byte date_and_time[7];
  date_and_time[0] = Decimal2BCD(sec); // 59秒
  date_and_time[1] = Decimal2BCD(min); // 59分
  date_and_time[2] = Decimal2BCD(hour); // 24時
  date_and_time[3] = Decimal2BCD(dd); // 31日
  date_and_time[4] = you; // 曜日 日=0,月=1,火=2,水=3,木=4,金=5,土=6,
  date_and_time[5] = Decimal2BCD(mm); // 12月
//  date_and_time[6] = 0x116;  //Decimal2BCD(yyyy-2000); // 年
  date_and_time[6] = yearDecimal2BCD( yyyy);  //年

  Rtc.sync(date_and_time);

}

LM350Tで可変電圧安定化電源
 
以前、壊れたパソコンから取り出した電源ユニットを使って

電源を作っていましたが、
これ、結構置きくて、邪魔なのと、
パソコン用の3.3V、5V、12Vしか取り出せないとか
少し不満があったので、
小さくてシームレスに電源電圧が変更できるやつを作成してみました。
使ったのはLM350Tです。
入力電圧が35Vまで、
出力はそれより少なくて
最大3Aまで
ってことなので、自分がやる実験には丁度いい感じと思い、試してみました。
 
回路図はデータシートのをそのままで
 

 
中身の図。今回は余っていたラグ端子を使ってみました。
 

手軽に組む場合はこれ良いですよ。
 
 

 

DRAGINO-LG01N-RoLaWANGWのテスト準備編
 
以前、arduinoでプログラムできるじゃんとか、日本の周波数の923メガで運用できるじゃんと思って
ポチっておいた、DRAGINO-LG01Nが届いたので今日、試しておりました。
 
で、結論から言うと、
新しいバージョンのDRAGINO-LG01Nはarduinoマイコンが搭載されておりません。
その為に
arduino yunのサービスによって、同じLANセグメントのマシンからはarduinoIDEからプログラム出来るはずだったんですけど、
出来ませんでした。という報告になります。
 
 
いやいや、arduinoを当てにしておりましたので買った後に真実が判り残念だったんですが。
今日一日それに時間を消費しましたので記録しておきます。
 

前の4系のファームまではarduino yunのIDEと連携する仕掛けが搭載されており、
日本語化されている説明書を参考に進めていたんですけど。
完成したスケッチをマイコンに送ることが出来ずに6時間試行錯誤。途中昼寝。な感じでしたが
結論としては上記の図の通り、大事なATMEGA328が搭載されていないと言う事ですから。ちょっと当てが外れた感じです。
 
その他、ファームのバージョンを下げると出来るのか?
 →ファームが下がらない。
とか
 単純な構成のLAN環境にしてみるとか
とかとか
いろいろやりましたが、結局、出来ないという結論で。
 
ご・・・・ざ・・・・い・・・・ま・・・・す・・・・。
 
ここまでだいぶ下げなレポートになりましたが、
このマシン結構よく出来ております。
 
中身はLinuxなので、実は簡単なファイアウォールぐらいはラクラクできそうな感じですし、
 400MhzのCPU
 64MのRAM
 100MのLAN
 802.11bgnのWiFi
 LoRaの機能
という感じでIoT向けとしてはいろいろ応用できそうなスペック満載。
ちょっと前に出たマシンで価格的にもそんなに高くはないので買いと思います。
 
その他、英語版の5系ファームの説明書から収穫有りましたので。
以下に書きます。
 
■判った1
 
このマシンはWIFIのアクセスポイントとして機能しており、
 初期設定から「dragino-」から始まるSSIDで接続が可能になっておりまして、
  パスワードはdragino+dragino」です。
  古いマニュアルを参考にやっておりましたらここで最初に引っかかりましたので。参考にどうぞ。
 
■判った2
 
 判った1 で書いたアクセスポイントにWIFI接続した場合は10.130.1.1でブラウザで接続すれば設定画面にログインできます。
 ID/パスはroot/dragino
 
■判った3
 
 いろいろ接続形態有り。
 設定画面からWIFIのSSIDとパスワードを入力して自宅WIFIに接続。
 このマシン、普通にルーターとして機能します。
 これは発見でした。
 WAN側の線を普通に上位LANにDHCPで接続したりして、(またはWIFIでも上位LANに接続すれば)
 LAN側では普通にネット使えます。
 ま、接続速度を考えるとそれだけで買う理由にはなりませんけど。
 そういう機能も有るってことで。使いようは有るってことです。
 LoRaも考えると割と高機能なルータになりそうです
 
■判った4
 
 ここから先が苦労した。
 WAN側を自宅LANに接続するわけだけど、
 自宅LANから普通にIPアドレス指定で、設定画面が見れないのですよ。
 で、夕方ぐらいに気づいてファームに応じたバージョンのマニュアル(英語)によると、
 っていうか、俺は英語を理解しない。
 google先生によると以下の通り。
  httpはポート8000
  sshはポート2222
 との事で、やっと魔法のシェルコマンドまでたどり着きました。
 

 
ここまで来れたら後はこっちのものだよね。
おれの手の内に落ちたなー「どらぎの」
という所です。
 

 

屋久島土産を人感センサー付きで光らせてみた。
 
この記事は「屋久島土産のライトアップ」の続編です。

 

 
前の記事はaitendoさんで見つけた1センチサイズのよく光るLEDで
屋久島土産を下からライトアップするという記事でした。
 
これはこれできれいに光ってくれたのでOKでしたが、
常時つけっぱなしにすると電池がすぐに無くなるので
実用的では有りませんでした。
 
そこで、今回は通常はセンサーモードになっており、
消費電流を20μAに押さえておいて、
人を見つけたときだけ10秒間程度ライトを点灯する事にして、
いちいち、電気をつけなくてもキレイっていう事にしたいと思います。
 
回路図
 

 
前の回路との違いはSB412Aを追加して、
SB412AのSignalの出力だけだと弱いので、トランジスター(2SC1815L)で増幅、
さらに、シグナルリレー(G3VM-21AR)で砲弾ダイオードをドライブできるようにします。
2SC1815Lの出力だけだと、いまいちLEDでの光が弱かったので、後ろの段に追加しました。
 
ビデオ
 
https://youtu.be/6WPfWujiB9o

風景

 
 

太陽電池充放電コントローラー-SolarAmpminiの修理。
 
秋月で買った通販コードM-06167太陽光パネルを接続して
バッテリーに充電したり、バッテリーの電気を使ったりする時に
自動で制御してくれる便利なやつ。
 

 
つなぎ方は簡単。
 

パーツは
【M-05360】 太陽電池モジュール(4W)
【M-06167】 太陽電池充放電コントローラ SolarAmp mini
【B-12927】 完全密封型鉛蓄電池 12V50Ah WP50-12
インバーターはテキトウで。
ソーラーパネルは1枚だと9Vなんで、2枚直列で最大18Vとしています。
 
バッテリーがフル充電になったのを確認して上の図のパソコンのところにデスクトップパソコンを接続。
電源ON。『ぐはー』とは聞こえませんでしたが
コントローラーちゃんがお亡くなりになりました。チーン。
で、買い直そうかなーなんて思っていましたが、
なんかもったいない気がして。捨てないで取っておきました。



・・・・それから・・・・一月ぐらい経過。
昨夜、夢を見た。『簡単に治るであろう。』と神のお告げが。
で、とりあえず、バラして、
 

 
神のお告げの通りバッテリーのラインからテスターを当てていくと、
1つ目の部品の所で電気が来ていない。
 

 
パーツの名前はKOAC 10A。ググるとヒューズだって。
単純に電流を流しすぎて安全回路のヒューズが切れたっぽい。
 
直結するとすぐにLEDが点灯して生き返ったので修理はこれでOKとするが、
内部にヒューズっていうのも使い勝手が良いとは言えないので、
 
外部にヒューズを増設することにしました。
 
P-09629 ヒューズホルダー(中継用) MF-520
P-07138 ガラス管ヒューズ MF61NR 250V10A 32mm