ラベル 電子工作 の投稿を表示しています。 すべての投稿を表示
ラベル 電子工作 の投稿を表示しています。 すべての投稿を表示

2026年10月4日日曜日

LINEに通知する見守りセンサ工作再開

昨日、東京は37日間連続の雨の日が途切れましたが、今日の午後さっそく雨、その後再び晴れ間が見えましたが。台風に刺激された秋雨前線が不安定なのでしょうか。そんな中、長らく保留状態だったLINEに通知する見守りセンサ工作を再開しました。

3月末にユニバーサル基板に載せ、タカチのケース加工をして、長期間の稼働テスト開始したところ、ちょっと不具合や使い勝手で改善の気付きがあり、そこで止まっていました。


ケースに開けた給電用のUSB Type-Cコネクタ穴にコネクタが合うように基板の取付穴を微調整。丸棒ヤスリで穴の形を加工。


USB ACアダプタからの給電で起動させ、WiFiに繋がり、緑色LEDが点灯したところ。これで見守り開始です。黄色のスイッチは旅行などで不在にするときに押してもらうことを考えていましたが、高齢者にその操作をしてもらうのは無理かな?と思い、まだ用途は決めてないです。LINEにメッセージが送れるかのテストボタンになりそう。


白いドームの焦電型赤外線センサで人の動きを検出します。キッチンなどに置いておくことで、もしも家のどこかで倒れたりして12時間とか24時間動きが検知できない場合に家族のLINEにメッセージを送ります。

LINEに見守り開始のメッセージが届きました。

最近視力が落ち気味で夜は工作をあまりしたくないですが、プログラム作成は大丈夫なので秋の夜長を楽しもうと思っています。

2026年10月2日金曜日

ギヤードステッピングモータ 28BYJ-48 を 2相バイポーラに改造

アマゾンなどで数百円で購入できるギヤードステッピングモータ 28BYJ-48(ユニポーラ)を2相バイポーラに改造したので備忘録。


・ステッピングモーター 28BYJ-48 5V ULN2003ドライバーボード付き 2個650円

・DC 5V ステッピングモータ+ 28BYJ-48 ULN2003ドライバーボード 4個1,195円


なぜ改造したかというと、M5Stack+XYZ軸ステッピングモータドライバを使ってバイポーラのステッピングモータと混在で制御したかったため。AIに聞いたらバイポーラ型に改造するのが簡単とのことで、その方法を教えてくれた。

まず青色のプラスティックカバーを取り外す。爪で嵌っているので隙間に薄いドライバを差し込んで、こじって開けた。爪の部分が割れたけど問題なし。


続いて赤色のコードが繋がっている真ん中のパターンをカットする。カッターで切れ目を入れて無理やりパターンを剥がした。加工が酷いが問題なし。


M5Stackのドライバモジュールへのつなぎ方は、橙色と桃色がA+/A-、黄色と青色がB+/B-、パターンをカットした赤色は使わない。


今回、利用する元々バイポーラのステッピングモータ(Z軸)は24V駆動なので、ドライバモジュールには24Vを印加する。しかし、改造した 28BYJ-48 は 5V仕様なのでそのままでは電流が流れすぎてコイルが焼き切れてしまう。

そこでモータドライバ上の半固定抵抗を回して電流を絞る。AIは最大でも0.15A程度になるようにとのこと。電流を測るのが面倒だったので、半固定抵抗を一番絞って(左に回し切って)から徐々に右に回して、モータが回転するところにもってきた。一番絞ったところから90度くらい右に回したあたりでよい感じ(下記写真は再現)。


こんな感じでXY軸を改造28BYJ-48で構成。
 

まだZ軸と24V電源は繋いでないが、パソコンからのUSBケーブルからの給電でM5Stackは動作してLCDに表示がでている。しかしこのままではモータは回らない。

以上

2026年9月27日日曜日

工作用工具を新調

久しぶりの工作ネタ。涼しくなってきて工作するのによい季節になりました。以前も書きましたが工作部屋にはエアコンが無いので、真夏や真冬の工作は厳しいものがあります。ということで工作を再開するにあたり、今回新たに購入した工作用の工具の紹介です。

まずは導電性マット。最近小さなビスや部品を机の上で見失うことが多く(机の上が乱雑ということもあるが、工作を始めると散らかる)、以前から導入しようかと思っていた。上に置いてある30cmの定規からサイズ感が分かるかと思いますが、幅45cmくらい  550×350mmの広いマットです。大き目の基板などを扱うことを考えてこれにしました。ちょっと匂うけど、そのうち揮発するでしょう。


クリップとマグネットがフレキシブルジョイントで繋がった部品押さえ治具。半田付けするときに基板や部品を空中で固定したい場合に利用予定。


マグネットなので定規などにも貼り付く。重さが必要な場合には鉄板などを使える。


最後は小型のバイス。強めに固定したい場合などに利用予定。簡単な作りなので、3Dプリンタで自作もできそうだけど買った方が安い。バイスの開閉がボタンを押すことでネジを回さなくても簡単にできるところがよい。もちろんネジを回して締めることもできる。


さて、何を作るかが課題。以前から進んでいない 見守りセンサ もそろそろ完成させたいし、色々と買ったままストックされている部品もあるので、なんとか活用せねば、と思う今日この頃。

2026年8月15日土曜日

2ch温度ロガー(デジタル温度センサ版)の回路図・スケッチ

夏休み、少し帰省していました。帰省してもやることは庭木の剪定、伐採、草むしり、除草剤撒きなど自宅と同じです。あとお墓参り。往路では大渋滞にはまり普段の2倍の時間がかかり、帰路では高速で台風一過の土砂降りに見舞われトホホです。

天気鍋は結構利用していますが、雲が多くて最高温度記録は更新できていません。最高温度に達する前にお昼のそうめんや冷や麦に使ってしまうのも一因ですが。今日は冷やし中華に利用。午前中、雲が多くて温度は60℃弱でした。

天気鍋の温度推移の記録に使っている2ch温度ロガーは昨年9月に作り始め、今年の2月には、回路図・スケッチをアップしていました(アップしたことを忘れていた)。ところが、温度センサの防水が破れて異常値を記録 するようになってしまったため、急遽 防水対応のデジタル温度プローブへの交換を行いました。最初からこれを使えばよかった。

以下、これまでの経緯です。

主な機能は以下の通り。
  • 測定の正確な日時はWiFi経由のntpサーバから取得
  • microSDのsetup.txtファイルに平文でWiFiのSSID,Password 及び測定時間間隔のデフォルトの番号を記載(0:1秒, 1:10秒, 2:30秒, 3:1分, 4:5分, 5:10分)
     例)
    SSID mySSID
    PW   myPassWord
    Interval 0
  • 時間間隔は電源投入後にタクトスイッチでも変更可能(5秒経過すると測定を開始)
  • WiFiがない環境で使う際は、タクトスイッチを押しながら電源オン
    WiWi設定があっても繋がらない場合には、複数回トライして諦める
    WiWiがない場合、或いは何度トライして繋がらない場合には日時を26年1月1日00:00:00とする
  • ログはmicroSD内にタイムスタンプ付のcsvファイルとして格納する
    WiFiに繋がない、繋がらない、ntpサーバに繋がらない際の日時は 2026-01-01 00:00:00となる
  • ファイル名が重複する場合にはファイル名に追番が付く
  • 測定は指定した時刻が時間間隔で正時になったとき
  • 例)10秒毎なら0秒、10秒、20秒…のとき、1分毎なら毎分0秒のとき
  • スリープから1秒毎の割り込みで目覚めて正時になったか確認(再びスリープ)
デジタル温度センサ(プローブ)へ交換したあとの回路図とスケッチです。無保証です。ハード、ソフトの修正をできるだけ少なく済ませたので無駄な処理などもあります。今ならAIがもっとスマートなコードを書いてくれそう。


//---------------------------------------------------------------------------------------
//                                                                         '26.07.29 naka
//                                                                         '26.01.31 naka
//                                                                   ver.0 '25.09.14 naka
//  ESP32 2ch温度ロガー(小型液晶に時刻も表示)
//
//  ・測定の正確な日時はWiFi経由のntpサーバから取得
//  ・microSDのsetup.txtファイルに平文でWiFiのSSID,Password 及び測定時間間隔の
//    デフォルトの番号を記載(0:1秒, 1:10秒, 2:30秒, 3:1分, 4:5分, 5:10分)
//    例)SSID mySSID
//        PW   myPassWord
//        Interval 0
//  ・時間間隔は電源投入後にタクトスイッチでも変更可能(5秒経過すると測定を開始)
//  ・WiFiがない環境で使う際は、タクトスイッチを押しながら電源オン
//  ・WiWi設定があっても繋がらない場合には、複数回トライして諦める
//  ・WiWiがない場合、或いは何度トライして繋がらない場合には日時を26年1月1日00:00:00とする
//  ・ログはmicroSD内にタイムスタンプ付のcsvファイルとして格納する
//  ・ファイル名が重複する場合にはファイル名に追番が付く
//  ・WiFiに繋がない、繋がらない、ntpサーバに繋がらない際の日時は"2026-01-01 00:00:00"となる
//  ・測定は指定した時刻が時間間隔で正時になったとき
//    例)10秒毎なら0秒、10秒、20秒…のとき、1分毎なら毎分0秒のとき
//  ・スリープから1秒毎の割り込みで目覚めて正時になったか確認(再びスリープ)
//
//  改版履歴
//  '26.07.29 センサをディジタルセンサDS18B20に変更
//---------------------------------------------------------------------------------------
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <ESP32Time.h>
#include <SD.h>
#include "FS.h"
#include "SPIFFS.h"
#include "esp_adc_cal.h"
ESP32Time rtc;

#define SD_CS 16   // SD card chip select
#define TACT_SW 25 // タクトスイッチ(WiFi未使用指示、測定時間間隔セット用)
const int SENSOR_PIN1 = 32; // 温度センサの出力ピンを接続
const int SENSOR_PIN2 = 33; // 温度センサの出力ピンを接続

esp_adc_cal_characteristics_t adc_chars;

#define SETUP_FILE "/setup.txt"
#include <FaBoLCDmini_AQM0802A.h>
FaBoLCDmini_AQM0802A lcd;

// デジタルセンサ
// https://github.com/PaulStoffregen/OneWire
#include <OneWire.h>
// https://github.com/milesburton/Arduino-Temperature-Control-Library
#include <DallasTemperature.h>
#define TEMPERATURE_PRECISION 12 // sensor resolution: 9 - 12bit

OneWire oneWireCh1(SENSOR_PIN1);
OneWire oneWireCh2(SENSOR_PIN2);
DallasTemperature sensorsCh1(&oneWireCh1);
DallasTemperature sensorsCh2(&oneWireCh2);
DeviceAddress tempDeviceAddressCh1;
DeviceAddress tempDeviceAddressCh2;

// microSDのsetupファイルに記載されているものが設定される
char ssid[24];
char password[24];

int interval;
int interval_i = 0;
const int interval_tbl[] = {1,10,30,60,300,600}; // 測定間隔(秒)
const int interval_tbl_size = 6;
char LogFile[64]; // 記録用のcsvファイル名(タイムスタンプから自動作成)

void setup()
{
  char msg[17];
  struct tm timeinfo;
  char logfilename[64];

  pinMode(TACT_SW, INPUT);

  // モニタLCD設定
  SetupLCD();
  delay(1000);

  // setupファイルを読み、WiFiのssid,password、測定間隔のデフォルトを取得
  if (!SD.begin(SD_CS, SPI, 24000000)) {
    dispLCD_msg("microSD failed  ","                ");
    while(true); 
  }
  read_setup_file(SETUP_FILE);

  bool wifi_flag = false;
  if (digitalRead(TACT_SW)==LOW) {
    dispLCD_msg("WiFi dosen't use","                ");
    while(digitalRead(TACT_SW)==LOW);
  }
  else {
    dispLCD_msg("Connecting WiFi ","                ");
    if (wifi_connect()) {
      wifi_flag = true;
    }
    else {
      dispLCD_msg("Connect failed  ","                ");
      delay(1000);
    }
  }
  delay(1000);

  if (wifi_flag) {
      // WiFi接続に成功した場合、NTPサーバに接続し、時刻設定
      configTime(9 * 3600L, 0, "ntp.nict.jp", "time.google.com", "ntp.jst.mfeed.ad.jp"); 
      int loopcnt = 0;
      while (!getLocalTime(&timeinfo)) {
        dispLCD_msg("Failed get time ","Retrying ...    ");
        if (++loopcnt>5) { // 念のため5回繰り返してダメなら抜ける
          wifi_flag = false;
          break; 
        }
        delay(500);
      }
    dispLCD_msg("                ","                ");
    wifi_disconnect(); // 以降はWiFi未接続にする
  }

  // 起動時にSWが押されていたか、WiFi接続に失敗か、ntpサーバからの時刻取得に失敗した場合は下記の日付と時刻設定
  if (!wifi_flag) {
    rtc.setTime(1767225600); // 2026-01-01 00:00:00		
  }

  // 測定時間間隔の設定(5秒無操作で戻ってくる)
  set_interval();

  // デジタルセンサ
  sensorsCh1.getAddress(tempDeviceAddressCh1, 0); // get device0 address
  sensorsCh1.setResolution(tempDeviceAddressCh1, TEMPERATURE_PRECISION); // set resolution
  sensorsCh2.getAddress(tempDeviceAddressCh2, 0); // get device0 address
  sensorsCh2.setResolution(tempDeviceAddressCh2, TEMPERATURE_PRECISION); // set resolution

  sensorsCh1.begin();
  sensorsCh2.begin();

  // ダミーで1回空読み(1発目に-127℃が返ってくることがあったので)
  sensorsCh1.requestTemperatures(); // request temp.
  float temp = sensorsCh1.getTempCByIndex(0); 
  sensorsCh2.requestTemperatures(); // request temp.
  temp = sensorsCh2.getTempCByIndex(0); 

  // ログファイル名を決めるためにタイムスタンプ取得
  getLocalTime(&timeinfo);
  sprintf(logfilename, "%02d%02d%02d_%02d%02d%02d.csv",(timeinfo.tm_year + 1900)%100, timeinfo.tm_mon + 1, timeinfo.tm_mday,timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);

  // WiFiに繋がない場合、時刻情報を取得できないためタイムスタンプのファイル名が同じになることを防ぐ。
  get_unique_filename("",logfilename,LogFile,sizeof(LogFile));

  // 測定開始メッセージ
  dispLCD_msg("Start           ","     measurement");

  // 1,000,000us = 1sのタイマー設定
  esp_sleep_enable_timer_wakeup(1000000);

}

void loop()
{ 
  // 温度を測定してログファイルに書き出す
  measurement(interval);

  // ライトスリープ(タイマー割り込みで1秒毎に目覚める)
  esp_light_sleep_start();
}

void set_interval() {
  disp_interval(interval_i); // 現在の設定時間を表示

  int prevmsec = millis();
  while ((millis() - prevmsec)<5000) {  // 5秒反応が無ければ時間設定を抜ける
    if (digitalRead(TACT_SW)==LOW) {
      interval_i++;
      prevmsec = millis();
      if (interval_i>=interval_tbl_size) interval_i = 0;

      disp_interval(interval_i); // 現在の設定時間を表示
      while (digitalRead(TACT_SW)==LOW) {}  // SWが離されるまで待つ
      delay(20); //チャタリング対策
      prevmsec = millis();
    }
  }

  interval = interval_tbl[interval_i];
  return;
}

void disp_interval(int i) {
  int time = interval_tbl[i];
  char msg[20];

  if (time<60)
    sprintf(msg,"%11d(sec)",time);
  else
    sprintf(msg,"%11d(min)",time/60);
  dispLCD_msg("set interval    ",msg);
}

void measurement(int period) {
  char yymmddhhmmss[17],hhmmss[10],timestamp[17];
  struct tm timeinfo;
  getLocalTime(&timeinfo);

  // 測定する時間なのか確認(割り込みは1秒毎なので)
  bool flag = false;
  if (period==1) flag = true;
  else if (period==10 && timeinfo.tm_sec%10==0) flag = true; 
  else if (period==30 && timeinfo.tm_sec%30==0) flag = true; 
  else if (period==60 && timeinfo.tm_sec==0)    flag = true; 
  else if (period==120 && timeinfo.tm_sec==0 && timeinfo.tm_min%2==0) flag = true; 

  if (flag) {
    sprintf(timestamp,"%02d:%02d:%02d",timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
    sprintf(yymmddhhmmss, "%02d%02d%02d %s",(timeinfo.tm_year + 1900)%100, timeinfo.tm_mon + 1, timeinfo.tm_mday, timestamp);

    float temp1 = sens_temp_digitalCh1();
    float temp2 = sens_temp_digitalCh2();
    char buff1[8],buff2[8],buff[24];
    dtostrf(temp1,4,1,buff1);
    dtostrf(temp2,4,1,buff2);
    sprintf(buff, "%4s%cC , %4s%cC",buff1,0xDF,buff2,0xDF);
    dispLCD_msg(yymmddhhmmss,buff);

    write_logfile(timestamp,buff1,buff2);  // microSDに書き込み
  }

}

float sens_temp_digitalCh1() {
  float temp;
  sensorsCh1.requestTemperatures();       // request temp.
  temp = sensorsCh1.getTempCByIndex(0);   // get temp.
  return temp;
}

float sens_temp_digitalCh2() {
  float temp;
  sensorsCh2.requestTemperatures();       // request temp.
  temp = sensorsCh2.getTempCByIndex(0);   // get temp.
  return temp;
}

void dispLCD_msg(char msg1[],char msg2[]) {
  lcd.clear();
  lcd.setCursor(0, 0); // Col,Raw
  lcd.print(msg1);
  lcd.setCursor(0, 1); // Col,Raw
  lcd.print(msg2);
}

void write_logfile(char* timestamp, char* buff1,char* buff2){
  char buff[32];
  File outputFile = SD.open(LogFile,FILE_APPEND);
  
  sprintf(buff,"%s,%s,%s",timestamp,buff1,buff2);
  outputFile.println(buff);
  outputFile.close();
}

void SetupLCD() {
  delay(150);
  lcd.begin();
  lcd.command(0x38);
  delay(1);
  lcd.command(0x39);
  delay(1);
  lcd.command(0x14);
  delay(1);
  lcd.command(0x71);
  delay(1);
//  lcd.command(0x51);  // 5V
  lcd.command(0x56);  // 3.3V
  delay(2);
  lcd.command(0x6c);
  delay(300);
  lcd.command(0x38);
  delay(1);
  lcd.command(0x01);
  delay(2);
  lcd.command(0x0c);
  delay(2);
}

// Wifi接続
bool wifi_connect() {
  char msg[128];
  bool connected = false;

  for (int i=0;i<3;i++) { // WiFi接続、3回繰り返す
    WiFi.begin(ssid, password);    
    long int StartTime=millis();
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        if ((StartTime+5000) < millis()) break; // 5秒待って繋がらないときは諦める
    } 
  
    if (WiFi.status() == WL_CONNECTED) {
      IPAddress ip = WiFi.localIP();
      sprintf(msg,"%d.%d.%d.%d",ip[0], ip[1], ip[2], ip[3]);
      dispLCD_msg("Connected IP    ",msg);
      connected = true;
      break;
    }
    delay(100);
  } 

  if (connected)
    return true;
  else {
    wifi_disconnect();
    delay(100);
    return false;
  }

}

void wifi_disconnect() {
  WiFi.disconnect(true);
  WiFi.mode(WIFI_OFF);
}

int read_setup_file(char* fileName) {
  char  buff[128];
  char  key[128],val[128];

  File setupFile = SD.open(fileName,FILE_READ);
  if (setupFile) {
    while (readLine(setupFile,buff)>=0) {
      if (buff[0]!='#') {
        sscanf(buff,"%s %s",&key,&val);
        if (strcmp(key,"SSID")==0) {
          strcpy(ssid,val);
        }
        else if (strcmp(key,"PW")==0) {
          strcpy(password,val);
        }
        else if (strcmp(key,"Interval")==0) {
          int ival;
          sscanf(val,"%d",&ival);
            if (ival>=0 && ival<interval_tbl_size) { // 異常値チェック
              interval_i = ival;
            }
          }
      }
    }
    setupFile.close();
  }

  return 0;
}

// microSDから1行読み取る
int readLine(File fp, char *buff){
  int i = 0;
  if(!fp.available()) {
    return -1;
  }
  while (fp.available()) {
    char c = fp.read();
    if (c=='\r');
    else if (c=='\n') break;
    else buff[i++] = c;
  }
  buff[i] = 0;
  
  return i;
}

/**
 * 重複しないファイル名を生成する関数 (ChatGPTに書いてもらった)
 * @param fs_prefix マウントポイント (例: "/spiffs")
 * @param base_name 元のファイル名 (例: "data.txt")
 * @param out_path  結果を格納するバッファ
 * @param max_len   バッファの最大サイズ
 */
void get_unique_filename(const char* fs_prefix, const char* base_name, char* out_path, size_t max_len) {
    char temp_path[128];
    char name_part[64];
    char ext_part[16];
    
    // 拡張子を探す
    const char *dot = strrchr(base_name, '.');
    if (dot) {
        size_t name_len = dot - base_name;
        strncpy(name_part, base_name, name_len);
        name_part[name_len] = '\0';
        strcpy(ext_part, dot);
    } else {
        strcpy(name_part, base_name);
        ext_part[0] = '\0';
    }

    // 最初は元の名前でチェック
    snprintf(out_path, max_len, "%s/%s", fs_prefix, base_name);
    struct stat st;
    int counter = 1;

    // ファイルが存在し続ける間、ループ
    while (SD.exists(out_path)) {
        // "パス/ベース名 + 数字 + 拡張子" を組み立て
        snprintf(out_path, max_len, "%s/%s_%d%s", fs_prefix, name_part, counter, ext_part);
        counter++;
        
        // 無限ループ防止(必要に応じて)
        if (counter > 999) break; 
    }
}

//---------------------------------------------------------------------------------------
// EOF
//---------------------------------------------------------------------------------------

2026年8月2日日曜日

夏休みの自由研究:ソーラークッカー 天気鍋(太陽追尾の評価)

温度ロガーのセンサを防水デジタル温度センサに交換して天気鍋を評価してみました。

朝8:30頃に評価を開始。この時点ではまだ太陽が東の方にあって陽が当たっていないけど、一応太陽の方を向けてセット。まだ太陽追尾のターンテーブルを制御するarduinoはブレッドボードのまま。効果が確認できたらユニバーサル基板に載せ替えるつもり。


お昼過ぎ、かなり熱くなってきて蓋に結露ができています。昼前あたりから時々雲が流れてきて直射日光が当たる時間が少なくなってきました。温度の記録更新は厳しそう。


温度変化の様子です。12:00頃に70℃に達した後はあまり上がりませんでした。最高水温は14:00過ぎに72.6℃を記録。残念ながらこれまでの記録75℃には達しませんでした。

交換した防水デジタル温度センサも今のところ調子よく動いています。


太陽追尾のターンテーブル
の効果は明確には分からない結果でしたが、昼頃には70℃になるので、そうめんや冷や麦を茹でるには十分です。今日は記録をとるためにお昼の麺類はパスしました。

また暑くて終日快晴が期待できる日があったら記録を取ってみます。普段は特に記録せずに、かみさんが天気鍋を普段使いする予定。

2026年7月30日木曜日

2ch温度ロガーの温度センサ交換

先日、天気鍋の水温測定中に異常値を記録した温度ロガーですが、レジンで封止した温度センサの防水が破れた模様だったので、最初から防水対応になっているデジタル温度センサに交換しました。ロガーの製作記事はこちら など。

写真は交換前のロガーと、新たに購入した防水デジタル温度センサです。ケーブルが予想より太い。これまでのアナログ温度センサの接続はコネクタにしてあったので容易に交換できます。ケース内左上のホットボンドで固めてあるのがピンヘッダを使ったコネクタ。


防水デジタル温度センサはDS18B20というものでアマゾンで購入。2つでよかったが3つで安かったので、予備を考えて3個入り。


信号線にプルアップ抵抗が必要だったので、ユニバーサル基板の切れ端に抵抗を載せてピンヘッダを挿してコネクタ代わり。半田付け箇所が切れないようにホットボンドで固めています。あと、ケーブルが太かったのでチャンネル毎に別のゴムブッシュを通しました。


プログラム変更は、アナログセンサをAD変換して温度を読んでいた部分を、1ワイヤーのデジタル通信での読み取りにした。このセンサは複数センサを並列に繋いでも個別のアドレス毎に読み取れるらしいが、今回はこれまで2ポート使っていたのでそのままチャンネル毎にポートを分けました。

測定している様子。ch2は手で触っているので高めに出ています。


防水性能はまだ分かりません。今週末、晴れたら天気鍋で試してみます。

2026年7月26日日曜日

夏休みの自由研究:ソーラークッカー 天気鍋(トラブル発生)

太陽を追尾するターンテーブルの調整は、ぼほよくて12時間で180度回転しました。今日は朝のうち晴れたり曇ったりだったので、あまり期待しないでターンテーブルに載せた天気鍋の評価です。写真はまだ南東の方を向いています。これが徐々に南に向いていく。


途中で温度を見たら、温度ロガーが200℃を越えていておかしなことになっていました。センサが壊れた?かと思い、急遽外気を測定していた他方のセンサも鍋に入れて測定を継続。

結果、当初から鍋に入れていたセンサの温度はずっと異常(グラフ橙色)、もう一方も終盤になって異常値を記録するようになっていました。昼にそうめんを茹でるために鍋を回収したので今日の最高温度は66.7℃付近。十分熱くなっていてソーラークッカーはちゃんと働いています。

鍋からセンサを取り出してみると、レジンで固めたセンサが柔らかくなっていて水が進入した気がします。こんなになるのかとちょっと意外。調べてみると、一般的なレジンの耐熱温度は70~80℃らしい。これではダメですね。繰り返し70℃前後のお湯の温度を計っていたので徐々に劣化したのかと思う。


耐熱レジンで固めようかと思ってアマゾンで調べたら結構高い。それではと防水仕様になっているデジタルセンサを使うことにしました。小型の金属チューブに入ったケーブル一体型のセンサです。今までのセンサはアナログだったので、ESP32で書いたロガーのプログラムを変更する必要がありますが、大した手間ではないと期待。ただ、これまでの記録と差が生じる可能性もある。

週末にはロガーの改修ができるかと思っています。その後、快晴で暑くなってくれるとよいのですが。

2026年7月25日土曜日

夏休みの自由研究:ソーラークッカー 天気鍋(太陽追尾ターンテーブル)

久しぶりにArduinoで電子工作、と言っても工作全体から占める割合は僅か。

天気鍋で80℃のお湯を沸かすことを目指して、太陽を追尾するターンテーブルを作ってみました。動力は昔アマゾンで買った小型のギヤ付きステッピングモータ、これをArduino nanoで動かします。タイヤはミニ四駆のものでホイールは3Dプリントしたもの。モータの取り付け部品も3Dプリント。

テーブルは家にあったべニア板を丸く切り出して、写真のような回転ベアリングを使います。これで水2.5L+鍋+透明バケツ+反射板を支えられるハズ。


横から見た様子。タイヤを天板に押し付けるためにスプリングを使っています。当初このスプリングが強すぎてタイヤが回らなかったので、少し切って短くしました。


水2.5L入りの天気鍋を載せて動くか確認のためにモータを早く回しています。アニメーションGIFになっています。この重量が乗ってもちゃんと動きました。

太陽の動きに合わせてモータの回転速度を調整します。1時間で15度回転させればよいので、あらかじめ概算のパルス間隔を出しておきました。タイヤの直径、テーブルにタイヤが接する半径、ステップ数とギヤードモータの減速による回転数などから計算しました。

中心に分度器を置き、テーブルに接しないように差し金を渡して計測開始。


3時間後の様子です。45度になればよいのですが、50度くらいまで進んでしまいました。組み立て時にタイヤが接する位置がテーブルの中心に寄ったようです。

50/45=1.11倍進んだので、ステッピングモータのパルス間隔を1.11倍遅くして調整。


水を入れた天気鍋を載せて12時間のテスト開始です。明日の朝5:15頃に180度回っていれば調整完了です(最近5:00前に目が覚める)。バケツの向こうに差し金が立てあり目印にしています。それが反対側のバケツの取っ手に一致すれば180度です。


上手く行って、明日晴れたらこのターンテーブルに反射板と天気鍋を載せて評価してみます。

2026年5月27日水曜日

非接触スイッチ使ったキッチンの手元灯製作記録【動画】

先日完成したToFセンサを使った非接触スイッチ を キッチンの手元灯に組み込む 過程の動画をアップしました。blog記事は分散しているので、一応のまとめとして作成。静止画が多く紙芝居的ですが、2分50秒ほどの短い動画です。


ナレーションに「ゆっくり」ではない最近の合成音声を使ってみました。短い動画なのにナレーション入れるの地味に大変。毎週のようにアップしているYouTuberはいつ寝ているの?

2026年5月17日日曜日

断捨離は続くけどお宝は見つからない

東京都心で今年初めての真夏日を記録したようですね。午前中は久しぶりに町田の市街地に出かけたのですが 、すでに暑かった。

主目的ではなかったのですが、プラバンが欲しくて久しぶりにハンズに寄ったらマテリアルや工具などはなくなっていて、少しおしゃれな雑貨の店といった感じ。ハンズはカインズに買収されたのでカインズとの棲み分けか? それではと、ジョルナという若い子向けのファッションビルの地下にあって以前はプラモやミニ四駆、工作材料を売っていた店に行ったら、すでに無くなっていて、トレーディングカード屋になっていた。最後に世界堂という画材屋さんに向かったら、そこも無くなっていた。手を動かして何か作ろうという人は減ってきているんだろうね。ただ、まだユザワヤは残っている模様。

さて昨日、TOFセンサがステンレスシンクに反射してLED照明が勝手にオンオフした問題は一応解決しました。LED照明を一旦取り外して内部のTOFセンサを水平ではなく、若干傾きをつけて斜めに取り付け。これによりステンレスシンクでの反射光がセンサに届かなくなったはず。再びキッチンに取り付けましたが誤反応はありません。

ところでGWからのかみさんの探し物はまだ見つかりません。色々な場所を探すついでにパソコン関係の断捨離も進めています。今回発掘されたのは、各種ストレージと大量のケーブル類。

右下は、SDカードの親玉のような薄くて大きなメモリカード64MB、なんという名前だろう? 昔のトイカメラに入っていた。これ以外はノートパソコンのPCカードスロットに挿すストレージ。左上は1.8インチHDD 5GB、そのとなりは10MBのストレージ、右端は3.5インチHDD(容量不明)。下はコンパクトフラッシュを挿したPCカード。


もうPCカードを挿せるノートパソコンは持ってないし、コンパクトフラッシュも読めない。何が入っていたか分からないのでみんな壊してから廃棄したい。その他、マウス、ケーブル、スピーカ、Hub、基板などを廃棄します。

前回お宝が発掘されて思い出したのですが、どこかに純金名刺もあるはず。その昔、仕事で田中貴金属さんの工場に伺ったときに頂いたものなので由緒正しい本物のはず。何も知らなかったのですが、普通に名刺交換して、会議を行い、退去する際に純金の名刺になったものをお土産として頂きました。

長らく忘れていたのですが、4~5年前に片づけをしていたときに見つけ、大事だから仕舞っておいたのですが、仕舞い場所を完全に忘れた。断捨離ついでに探しているけど見つからない。何かに紛れて捨ててしまった恐れもある。

純金名刺は金が0.5gか1gらしいので、今の価格だと13,000円か26,000円の価値があります。一時期3万円/g近いこともあった。

ということで、探し物と断捨離はまだしばらく続きそう。


2026年5月16日土曜日

TOFセンサ照明に伏兵あり

先週取り付けたTOFセンサにより非接触でオンオフできるLED照明ですが、思わぬ伏兵がいました。まぁ思いもよらないから伏兵なんですが。

それは炊飯器の「湯気」です。TOFセンサの真下ではないのですがキッチンカウンタには炊飯器が置いてあり、ご飯が炊けて炊飯器から湯気が出始めると、照明が勝手にオンオフを繰り返します。夕方気付きましたが朝もそうだと思う。センサのレーザ光が湯気に反射して認識している模様。

という訳で、照明の左右を入れ替えてTOFセンサを左側に持ってきました。また、少し手前に取り付けて手を奥まで伸ばさなくて済むように少し改善。かみさんから届きにくいという声があったので。照明の取り付け方の変更は簡単でしたが、棚の中に隠れている電気配線を見直すのに少し苦戦。


奥に以前の照明が取り付けたあった穴が見えています。これを隠すために照明機器を奥に取り付けていたのです。

これで湯気問題は解決した、と思ったら今度は何もないのにオンオフする現象が発生。何事かと思ったら、取り付けを変更したことでセンサがステンレス製シンクの上に来たので、これに反射しているようです。シンク迄は1メートル以上あるので近接センサが反応する距離ではないはず。しかし布巾でシンクを隠したら反応しなくなりました。ステンレスの反射率が高いので距離の測定が正しくできていないと思われます。

常に布巾で覆う訳にもいかず、どうするか検討中。センサを少し斜めにして真下ではなくシンクの手前に向くようにするのがよさそう。しかし、狭いところに組み込んであるので斜めにできるか分かりません。実用化までは色々あります。今日は疲れたのでここまで。

2026年5月9日土曜日

TOFセンサ近接スイッチ組み込みLED照明の取り付け

連休も残すところ後1日、12連休も過ぎてみればあっと言う間でした。 そんな中ようやく、TOFセンサ近接スイッチでオンオフするLED照明 をキッチンに取り付けました。

シンクの上にある棚の底面に付いていた古い蛍光灯照明を外すと、少し日焼けしたような感じだったので棚の底面や側面を塗装をし直すところから開始。写真はまだマスキングテープが貼ってあります。


近接スイッチを組み込んだLED照明を取り付けた様子。右端にパイロットランプの緑色LEDが点灯していますが、この写真ではわからないですね。

点灯中。今までの蛍光灯よりかなり明るくて、iPhoneの絞り機能が効いて窓の外が暗く映っています。


オンオフの様子をアニメーションGIFで。iPhoneを左手で持って撮影しているので揺れています。

これで調理中や洗い物で手が濡れていたりしていても、約18cm以内に手をかざすと照明のオンオフができます。手でなく野菜や包丁でもOK。包丁は振り回すと危ないが。

最初にTOFセンサを試してみたのが1月末で、それから長いことかかりましたがようやく完了です。以下、これまでの経緯。


2026年4月30日木曜日

1万円の超音波カッターを試す(動画)

12連休の2日目ですが、もう2日も経って残り10日か、と思うとあっという間に過ぎ去りそう。昨日今日は天気がイマイチですが、涼しいので草むしりや剪定などを行ってます。雑草の伸び方がヤバい。

前回書いた1万円の超音波カッター を実際に使ってみました。インターバルタイマ に使っているタカチのケースへの穴開けです。とても楽に切れていい感じ。ただ、溶けた樹脂が盛り上がるので寸法ギリギリは狙えない。少しマージンをみて最後はヤスリやアートナイフで整えた方がよい。

動画(2分半)を撮りましたが、刃先にピントが合わず加齢の進んだ手ばかりよく映っています(^_^;


明日は見守りセンサをなんとかしようと思います。どうも不在スイッチを押したあとに思いがけない動作をしているような感じ。不在のリミットを過ぎても見守りが正しく再開されない模様。

その後は、非接触スイッチを組み込んだLED照明をキッチンに取り付ける作業が待っています。こちらは、取り付けようとしている場所が既存の蛍光灯で焼けたような感じになっているので、まずは塗装をし直そうと考えています。明日にはマスカーテープと塗料が届くはず。

さらに庭木の剪定もまだ残っているし、越冬のためにリビングに避難していた観葉植物を外に出すのと、伸びすぎた枝を切って挿し木にしたい。また、高圧洗浄機ケルヒャーで掃除したいところもある。

そんなこんなの連休になりそう。

2026年4月25日土曜日

1万円の超音波カッター

今日は久しぶりの休日出勤、明日も出勤。その代わり30日、1日に代休とって、さらに5月7日、8日に年次休暇で、4 月29日から5月10日まで怒涛の12連休。途中帰省するけどあとはノープラン。溜まっている工作を何とかしたい。

さて、いつものようにアリエクを彷徨っていたら、1万円弱で購入できる超音波カッターがあったのでポチってみた。昨日届いていたけど開ける暇がなく、先ほど開封。格好いい化粧箱に入っています。型名はFS-S1 Ultrasonic Cutting Knife というもの。昔の超音波カッターと比べるとよい感じのデザイン。USB-TypeC PD 15Vで動作。


中身はこんな感じで、USB-TypeCのACアダプタは15V 3Aまで出せる。PDなので5V, 9V, 15V, 20VもOKらしい。替え刃が沢山入っていて嬉しい。ビニール袋の中身は刃を取り付けるための六角レンチとイモネジなど。


受け取り確認・検収のために手元にあった薄いプラスティックで試したらサクッと簡単に切れた。これまでプラケースへの角穴はΦ3mmのドリル穴を並べて開けてから繋いでカットして、ヤスリ掛けしていたけど、これでかなり楽になりそう。

後日、実際に角穴を開けるたときに使い勝手などをアップするつもり。


2026年4月1日水曜日

LINEに通知する見守りセンサ、最終テスト継続中

日曜日から開始したLINE見守りセンサは、普段使っていない部屋に置いてあり、見守り継続中です。昨日はその部屋に入るのが遅れてしまい、15時間無反応が検出されてしまいました。うまく動いていることが確認できたのでOKです。

今日は、毎月1日の12:00に見守り継続中であることを知らせる通知機能が無事に動作して、メッセージが届きました。職場で昼休みに確認して小さくガッツポーズ。LINEのトーク画面に桜の花びらが舞っています。

あとは、外出(旅行)スイッチが押されたあと、帰宅反応があるまで見守りを保留する機能と、帰宅しなくても4日経過すると見守りが自動復帰する機能の確認をしていきます。自動復帰は4日後(24Hx4)にしていますが、自動復帰からさらに15時間反応がなかったときにメッセージが飛びます。計111時間後ですね。このあたりを確認し、一通りの検証が終わったらスケッチと回路図をアップするつもりです。

ちなみに、ESP32開発キットを使ったプロトタイプのスケッチは以下にあります。機能的にはほぼ同じ。

早いところ仕上げないと1年がかりのプロジェクトになってしまう。


2026年3月29日日曜日

LINEに通知する見守りセンサの最終テスト開始

急に暖かくなってお花見日和でした。しかし相変わらず花粉症が酷いので、遠出は避けて近所で軽くお花見。

今朝、見守りセンサに最終プログラムを書き込み、テストしようとしたら動作中を示す緑色LEDが点灯しません。プログラムの不具合かと調べましたが明らかに光るはず。基板の裏からLED接続箇所をテスタで測ったら異常です。念のためLEDテスタで電圧をかけてみましたが点灯しません。ユニバーサル基板に繋いだのは新しいLEDだったと思うのですが、壊れていたようです。滅多にないです。

ということで朝からLEDを交換してようやく長時間のテスト開始。主に以下の内容を確認します。

  • 15時間反応がないとLINEにメッセージが飛ぶ。15時間以内にセンサの前を横切るとカウンタをリセットする
  • 外出(旅行)スイッチを押しても4日以上経過すると見守りを再開する。よって4日+15時間でメッセージが飛ぶ
  • 毎月1日の12:00に見守り継続中である旨のメッセージが飛ぶ。
写真は見守り中であることを示す緑LED点灯中。異常を検知するまでの15時間、検知されないように普段使っていない部屋に置いてあります。これから数日は朝晩、その部屋に入って生きていることを感知させて、4月1日にくるはずのメッセージ後に、異常検知や外出モードの確認などを行うつもり。

見守り開始時に飛んできたメッセージ。


という訳で、しばらく実時間でのテストを続けます。毎月1日に飛んでくるはずのメッセージが届くか、3日後が楽しみです。

2026年3月28日土曜日

TOFセンサ近接スイッチでオンオフするLED照明(完成)

TOFセンサを使った近接スイッチでLED照明のオンオフを行う工作もいよいよ大詰め。 

先日届いた小型の電源モジュール にAC100Vを繋いで動作確認したら全然電圧がでません。壊れているのかと思い、もう1個調べても同じ。調べてみると購入したショップの商品説明には記載がなかったのですが、他のショップの説明欄を見ると、外付けに耐圧の大きなコンデンサ(物理的にも大きくなる)が必要なことが判りました。これでは小さく作れません。トホホです。

諦めて当初の計画通りトランスレス電源で行くことにしました。TOFセンサが発光するタイミングで電位が下がるため、モニタ用に点灯させている白色LEDがチラチラします。そこで白色LEDはやめて緑色にしたら若干目立たなくなったのでOKとしました。

写真はLED照明の端カバーに基板を収めた様子です。照明の拡散カバーの切り欠きはTOFセンサと干渉するためカットした跡です。


LED照明の端はこんな形のコネクタ。


全長は120cmあります。電源ケーブルは付属していた中間スイッチ入りケーブルです。通常はそのスイッチでオンオフします。


通電中、緑LEDが点灯します。このLEDが若干チラチラする。


SSRのLEDと逆向きに繋いであるので、照明点灯中は緑色LEDは消えます。


動作中の様子です。


今日はここまで。キッチンに取り付ける工事はまた別途。

参考にされる方がいるか不明ですが、回路図(クリックで拡大)です。途中、色々変更したので汚くて恐縮です。無保証です。AC100Vを扱うので参考にされる方は十分ご注意ください。ヒューズ必須です。

また、1uF250Vコンデンサに並列に入っている1MΩの抵抗は、コンデンサに溜まった電荷を逃がすためのもの。これがないとコンセントから抜いても電荷が残ってしまいプラグを触ったりすると感電する可能性があります。


プログラム(PIC アセンブラ)です。こちらも無保証です。著作権等は留保しますが、流用・改造などご自由にどうぞ。



;---------------------------------------------------------------------
; TOF近接センサVL6180Xを使った非接触スイッチ
;
;                                                      2026.02.22 naka
;  1. 概要
;     近接センサの前で手をかざすとオンオフ動作するスイッチ
;     ・距離は180mm位まではOK(動作確認時190mm程度までは反応した)#defineで設定
;     ・スイッチ動作はオルタネート(一度近づくとオン、一旦離れてもう一度でオフ)
;     ・距離測定は0.1秒毎、オンオフ動作時は1秒後に確認(チャタリング的な動作を避けるため)
;
;  2. ピンアサイン
;
;    (1). GP0  [in] : (ICSP DAT)
;    (2). GP1  [in] : (ICSP CLK)
;    (3). GP2  [out]: LED(デバッグ時に使用;最終的にSSRに置き換える)
;    (4). GP3  [in] : (ICSP VPP)
;    (5). GP4  [in] : I2C SDA
;    (6). GP5  [in] : I2C SCL
;
; 3. 備考
;   ・動作クロック :内蔵OSC 4MHz
;   ・電源         :3V(TOFセンサが2.8-3.0Vなので)
;   ・I2Cの応答待ちなどによる無限ループ対策にWDTを利用
;
; 4. 改版履歴
;    2026.02.14 new
;    2026.02.22 色々修正
;
;---------------------------------------------------------------------
;---------------------------------------------------------------------
;  デバイス定義
;---------------------------------------------------------------------
	LIST    P=12F629
	INCLUDE <P12F629.INC>
	__CONFIG _INTRC_OSC_NOCLKOUT & _WDT_ON & _PWRTE_ON & _MCLRE_OFF & _BODEN_OFF & _CP_OFF
	ERRORLEVEL	-302	;アセンブル時のバンク警告メッセージ抑制

;---------------------------------------------------------------------
;  ピン定義
;---------------------------------------------------------------------
#define	LED	2
#define	SDA	4
#define	SCL	5

;---------------------------------------------------------------------
;  TOFセンサ VL6180X I2Cアドレス
;---------------------------------------------------------------------
#define TOFADDR	0x52

;---------------------------------------------------------------------
;  TOFセンサが反応する距離(mm) これより近いとオン/オフする
;---------------------------------------------------------------------
#define ACTDIST	D'180'

;---------------------------------------------------------------------
;  変数レジスタの定義
;---------------------------------------------------------------------
WAIT1	EQU	H'20'
WAIT2	EQU	H'21'
CNT1	EQU	H'22'
CNT2	EQU	H'23'
OFFSET	EQU	H'24'
RANGE	EQU	H'25'
TIMOUTH	EQU	H'26'
TIMOUTL	EQU	H'27'
TOGGLE	EQU	H'28'	; 0ビット:トグル、1ビット:前回状態
; H'40'からI2C通信に利用(下の方で定義)
;
;---------------------------------------------------------------------
;  マクロ
;---------------------------------------------------------------------
	; TOFセンサ8ビットレジスタ設定
	
REG8BIT	MACRO	IDXH,IDXL,DATA
	RETLW	IDXH
	RETLW	IDXL
	RETLW	DATA
	ENDM
	
	; TOFセンサ16ビットレジスタ設定
REG16BIT MACRO	IDXH,IDXL,DATAH,DATAL
	RETLW	IDXH
	RETLW	IDXL
	RETLW	DATAH
	RETLW	DATAL
	ENDM
	;
	; TOFセンサレジスタ書き込み
WTREG	MACRO	HIGH,LOW,DATA
	MOVLW	TOFADDR
	CALL	I2CWSTART
	MOVLW	HIGH
	MOVWF	datao
	CALL	TX
	MOVLW	LOW
	MOVWF	datao
	CALL	TX
	MOVLW	DATA
	MOVWF	datao
	CALL	TX
	CALL	I2CSTOP
	ENDM

RDREG	MACRO	HIGH,LOW
	MOVLW	TOFADDR
	CALL	I2CWSTART
	MOVLW	HIGH
	MOVWF	datao
	CALL	TX
	MOVLW	LOW
	MOVWF	datao
	CALL	TX
	CALL	I2CSTOP
	;
	MOVLW	TOFADDR
	CALL	I2CRSTART
	CALL	RX
	CALL	I2CSTOP
	ENDM
	;
;---------------------------------------------------------------------
;  リセットベクタ
;---------------------------------------------------------------------
	ORG	00H		; リセット時の飛び込み先
	GOTO	MAIN		;
	;
	ORG	04H		; 割り込み時の飛び込み先
;---------------------------------------------------------------------
;  割り込み処理
;---------------------------------------------------------------------
INTRUPT
	RETFIE
;---------------------------------------------------------------------
;  メイン処理
;---------------------------------------------------------------------
MAIN
	CALL	DEVINIT
	;
	CALL	VL6180INIT
	CALL	VL6180CONFIG
	;
	CLRF	TOGGLE
	;
MAIN_LP
	CLRF	RANGE
	CALL	READRANGE	; 測定
	;
	MOVF	RANGE,W
	SUBLW	ACTDIST		; アクティブか否かチェック
	BTFSS	STATUS,C
	GOTO	INACT
	; 前回もアクティブなら何もしない(そうしないと点滅する)
	BTFSC	TOGGLE,1
	GOTO	NEXTREAD
	BSF	TOGGLE,1	; 前回アクティブではないのでトグル動作
	GOTO	TOGGLESW
	;
INACT
	BCF	TOGGLE,1
	GOTO	NEXTREAD

TOGGLESW
	MOVF	TOGGLE,W
	XORLW	H'01'
	MOVWF	TOGGLE
	;
	BTFSS	TOGGLE,0
	GOTO	$+3
	BSF	GPIO,LED
	GOTO	$+2
	BCF	GPIO,LED
	;
	CALL	W05S		; トグル動作時は1秒待つ
	CALL	W05S
	GOTO	MAIN_LP
	;
NEXTREAD
	CALL	W100MS		; 100ミリ秒ごとに測定
	CLRWDT
	GOTO	MAIN_LP
	;
;---------------------------------------------------------------------
;  初期化
;---------------------------------------------------------------------
DEVINIT
	CLRF	INTCON		;全割込み禁止
	;
	BANKSEL CMCON
	MOVLW	B'00000111'	; GP0-2をデジタルI/Oに設定
	MOVWF	CMCON		; デフォルトはコンパレータ

;	BANKSEL	1
;	CALL	3FFH		; 内部OSCのキャリブレーションデータ
;	MOVWF	OSCCAL		; OSCCALレジスタにセット

	BANKSEL	TRISIO
	MOVLW	B'00000011'	; RA0,RA1 input
	MOVWF	TRISIO
	BANKSEL GPIO
	;
	BANKSEL	0
	CLRWDT			; Clear WDT
	CLRF	TMR0		; Clear TMR0
	;
	BANKSEL	OPTION_REG
	MOVLW	b'00101101'	; WDTプリスケーラ 1:32 (576ms) 
	MOVWF	OPTION_REG
	BANKSEL	0
	;
	BCF	GPIO,LED
	;
	RETURN
	;
;---------------------------------------------------------------------
;  VL6180レジスタindex (#define名は32文字まで)
;---------------------------------------------------------------------
#define	READOUT__AVERAGING_SAMPLE_PERIOD	0x01,0x0A
#define	SYSALS__ANALOGUE_GAIN			0x00,0x3F
#define	SYSALS__INTEGRATION_PERIOD		0x00,0x40
#define	SYSALS__INTERMEASUREMENT_PERIOD		0x00,0x3E
#define	SYSRANGE__VHV_REPEAT_RATE		0x00,0x31
#define	SYSRANGE__VHV_RECALIBRATE		0x00,0x2E
#define	SYSRANGE__INTERMEASUREMENT_PERIO	0x00,0x1B
#define	SYSRANGE__MAX_CONVERGENCE_TIME		0x00,0x1C
#define	SYSTEM__INTERRUPT_CONFIG_GPIO		0x00,0x14
#define	SYSTEM__INTERRUPT_CLEAR			0x00,0x15
#define	INTERLEAVED_MODE__ENABLE		0x02,0xA3
#define	RESULT__RANGE_STATUS			0x00,0x4D
#define	RESULT__INTERRUPT_STATUS_GPIO		0x00,0x4F
#define SYSRANGE__START				0x00,0x18
#define	RESULT__RANGE_VAL			0x00,0x62
;
;---------------------------------------------------------------------
;  VL6180初期化(メモリ節約のためベタ書きではなくループを回した)
;---------------------------------------------------------------------
VL6180INIT
	CLRF	OFFSET
	CALL	REG8BITDT
	MOVWF	CNT1		; 設定パラメタ数
VLINIT_LP1
	MOVLW	H'52'		; アドレス
	CALL	I2CWSTART
	MOVLW	D'3'		; indexHigh, indexLow, data の3バイトを送る
	MOVWF	CNT2
VLINIT_LP2
	INCF	OFFSET,F
	CALL	REG8BITDT
	MOVWF	datao
	CALL	TX		; I2C送信
	DECFSZ	CNT2,F
	GOTO	VLINIT_LP2
	;
	CALL	I2CSTOP
	;
	DECFSZ	CNT1,F
	GOTO	VLINIT_LP1
	;
	RETURN
REG8BITDT			; 以下のテーブルから順に取り出す
	ORG	H'100'
	MOVLW	H'01'
	MOVWF	PCLATH
	MOVF	OFFSET,W
	ADDWF	PCL,F
	RETLW	D'31'		; 31パラメタ
	REG8BIT	0x02,0x07,0x01	; indexHigh, indexLow, data
	REG8BIT	0x02,0x08,0x01
	REG8BIT	0x00,0x96,0x00
	REG8BIT	0x00,0x97,0xFD	; RANGE_SCALER = 253
	REG8BIT	0x00,0xE3,0x01
	REG8BIT	0x00,0xE4,0x03
	REG8BIT	0x00,0xE5,0x02
	REG8BIT	0x00,0xE6,0x01
	REG8BIT	0x00,0xE7,0x03
	REG8BIT	0x00,0xF5,0x02
	REG8BIT	0x00,0xD9,0x05
	REG8BIT	0x00,0xDB,0xCE
	REG8BIT	0x00,0xDC,0x03
	REG8BIT	0x00,0xDD,0xF8
	REG8BIT	0x00,0x9F,0x00
	REG8BIT	0x00,0xA3,0x3C
	REG8BIT	0x00,0xB7,0x00
	REG8BIT	0x00,0xBB,0x3C
	REG8BIT	0x00,0xB2,0x09
	REG8BIT	0x00,0xCA,0x09
	REG8BIT	0x01,0x98,0x01
	REG8BIT	0x01,0xB0,0x17
	REG8BIT	0x01,0xAD,0x00
	REG8BIT	0x00,0xFF,0x05
	REG8BIT	0x01,0x00,0x05
	REG8BIT	0x01,0x99,0x05
	REG8BIT	0x01,0xA6,0x1B
	REG8BIT	0x01,0xAC,0x3E
	REG8BIT	0x01,0xA7,0x1F
	REG8BIT	0x00,0x30,0x00
	REG8BIT	0x00,0x16,0x00	;SYSTEM__FRESH_OUT_OF_RESET
	;
;---------------------------------------------------------------------
;  VL6180 configureDefault
;---------------------------------------------------------------------
VL6180CONFIG
	CLRF	OFFSET
	CALL	CONFIGDT
	MOVWF	CNT1		; 設定パラメタ数
VLCONFIG_LP1
	MOVLW	H'52'		; アドレス
	CALL	I2CWSTART
	MOVLW	D'3'		; indexHigh, indexLow, data の3バイトを送る
	MOVWF	CNT2
VLCONFIG_LP2
	INCF	OFFSET,F
	CALL	CONFIGDT
	MOVWF	datao
	CALL	TX		; I2C送信
	DECFSZ	CNT2,F
	GOTO	VLCONFIG_LP2
	;
	CALL	I2CSTOP
	;
	DECFSZ	CNT1,F
	GOTO	VLCONFIG_LP1
	;
	; テーブルのラストにある16bitパラメタ1個のみ
	MOVLW	D'4'		; indexHigh, indexLow, dataHigh, dataLow の4バイトを送る
	MOVWF	CNT2
VLCONFIG_LP3
	INCF	OFFSET,F
	CALL	CONFIGDT
	MOVWF	datao
	CALL	TX		; I2C送信
	DECFSZ	CNT2,F
	GOTO	VLCONFIG_LP3
	;
	CALL	I2CSTOP
	;
	RETURN
	;
CONFIGDT			; 以下のテーブルから順に取り出す
	MOVLW	H'01'
	MOVWF	PCLATH
	MOVF	OFFSET,W
	ADDWF	PCL,F
	RETLW	D'9'		; 8bit 9パラメタ + 16bit 1パラメタ
	REG8BIT	READOUT__AVERAGING_SAMPLE_PERIOD,0x30
	REG8BIT	SYSALS__ANALOGUE_GAIN,0x46
	REG8BIT	SYSRANGE__VHV_REPEAT_RATE,0xFF
	REG8BIT	SYSRANGE__VHV_RECALIBRATE,0x01
	REG8BIT	SYSRANGE__INTERMEASUREMENT_PERIO,0x09	; 100ms
	REG8BIT	SYSALS__INTERMEASUREMENT_PERIOD,0x31	; 500ms
	REG8BIT	SYSTEM__INTERRUPT_CONFIG_GPIO,0x24
	REG8BIT	SYSRANGE__MAX_CONVERGENCE_TIME,0x31	; 49ms
	REG8BIT	INTERLEAVED_MODE__ENABLE,0x00
	REG16BIT SYSALS__INTEGRATION_PERIOD,0x00,0x63	; 16bitパラメタ(1個のみ)
	;
;---------------------------------------------------------------------
;  VL6180 readRange
;---------------------------------------------------------------------
READRANGE
	; デバイスがreadyになるまで待つ
	RDREG	RESULT__RANGE_STATUS
	MOVF	datai,W
	ANDLW	H'01'
	BTFSC	STATUS,Z
	GOTO	READRANGE
	;
	; 測定開始フラグ設定
	WTREG	SYSRANGE__START,0x01
	;
	CLRF	TIMOUTH		; タイムアウトカウント用
	CLRF	TIMOUTL
READRANGLP
	CALL	CHKTIMEOUT	; タイムアウト確認
	BTFSS	STATUS,Z
	GOTO	READRANGE2
	MOVLW	D'255'		; タイムアウト
	MOVWF	RANGE
	RETURN
	;
READRANGE2
	; 測定完了確認
	RDREG	RESULT__INTERRUPT_STATUS_GPIO
	MOVF	datai,W
	ANDLW	0x07
	SUBLW	0x04
	BTFSS	STATUS,Z
	GOTO	READRANGLP
	;
	; 値を読み出す
	RDREG	RESULT__RANGE_VAL
	MOVF	datai,W
	MOVWF	RANGE
	;
	; 割り込みフラグクリア
	WTREG	SYSTEM__INTERRUPT_CLEAR, 0x01
	;
	RETURN
	;
;---------------------------------------------------------------------
;  センサのタイムアウトチェック(500ms)
;  1回の測定にざっと200usかかるとして 500ms/200us ... TIMOUTH=10
;---------------------------------------------------------------------
CHKTIMEOUT
	MOVF	TIMOUTL,W
	ADDLW	D'1'
	MOVWF	TIMOUTL
	BTFSS	STATUS,Z
	RETURN
	;
	MOVF	TIMOUTH,W
	ADDLW	D'1'
	MOVWF	TIMOUTH
	SUBLW	D'10'		; Zフラグを持ってリターン
	RETURN
	;
;---------------------------------------------------------------------
;  ウェイト色々
;---------------------------------------------------------------------
W1MS
	MOVLW	D'110'
	MOVWF	WAIT1
W1MSLP
	GOTO	$+1
	GOTO	$+1
	GOTO	$+1
	DECFSZ	WAIT1,F
	GOTO	W1MSLP
	GOTO	$+1
	GOTO	$+1
	NOP
	RETURN
	;
W100MS
	MOVLW	D'100'
	MOVWF	WAIT2
W100MSLP
	CALL	W1MS
	DECFSZ	WAIT2,F
	GOTO	W100MSLP
	CLRWDT
	RETURN
	;
W05S
	CALL	W100MS
	CALL	W100MS
	CALL	W100MS
W200MS	CALL	W100MS
	CALL	W100MS
	RETURN
	;
;---------------------------------------------------------------------
;  I2Cアクセスルーチン
;  流用元:Microchip Applicatin Note : AN982
;          Interfacing I2C Serial EEPROMs to PIC10 and PIC12 Drivers
;---------------------------------------------------------------------
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;
;   Filename:           i2c_routines.inc
;   Date:               March 21, 2005
;   File Version:       1.0
;   Assembled using:    MPLAB IDE 7.00.00.0
;
;   Author:             Chris Parris
;   Company:            Microchip Technology, Inc.
;
;*******************RAM register definitions**********************
    #define STARTRAM 0x40       ; Start RAM at address 0x40

    cblock  STARTRAM
        buffer                  ; Transfer/Receive bit buffer
        datai                   ; Data input byte buffer
        datao                   ; Data output byte buffer
        bitcount                ; Bit loop counter
        bytecount               ; Byte loop counter
        addressH                 ; Word address pointer
        addressL                 ; Word address pointer
        loops                   ; Delay loop counter
        loops2                  ; Delay loop counter
        pollcnt                 ; Polling loop counter
        i2caddr
    endc
;*******************Bit definitions*******************************
DO          equ     0           ; TX/RX buffer output bit
DI          equ     1           ; TX/RX buffer input bit
ACKB        equ     2           ; ACK/NO ACK select bit
;*******************Delay Macros**********************************
;           Each macro delay for 1 inst. cycle less than
;           required, as the instruction immediately
;           before the macro call provides 1 inst. cycle.
;*****************************************************************
THIGH   macro                   ; Clock high time delay (5 us)
        goto    $+1             ; 2-inst. cycle delay (2 us)
        goto    $+1             ; 2-inst. cycle delay (2 us)
        nop			; 1us
        endm

THDSTA  macro                   ; Start condition hold time delay (5 us)
        goto    $+1             ; 2-inst. cycle delay (2 us)
        goto    $+1             ; 2-inst. cycle delay (2 us)
        nop			; 1us
        endm

TSUSTA  macro                   ; Start condition setup time delay (5 us)
        goto    $+1             ; 2-inst. cycle delay (2 us)
        goto    $+1             ; 2-inst. cycle delay (2 us)
        nop			; 1us
        endm

TSUSTO  macro                   ; Stop condition setup time delay (5 us)
        goto    $+1             ; 2-inst. cycle delay (2 us)
        goto    $+1             ; 2-inst. cycle delay (2 us)
        nop			; 1us
        endm

TAA     macro                   ; Output valid from clock delay (4 us)
        goto    $+1             ; 2-inst. cycle delay (2 us)
        goto    $+1             ; 2-inst. cycle delay (2 us)
        endm
;-------------------------------------------------------------------
;  I2Cアクセス
;-------------------------------------------------------------------
I2CWSTART
	MOVWF	i2caddr		; I2Cアドレス退避
	call    BSTART              ; Generate start bit
                                    ; Now send the control byte
                                    ; for a write, to set address
	movf    i2caddr,W		    ; I2Cアドレス
	movwf   datao               ; Copy control byte to buffer
	call    TX                  ; Output control byte to device
	RETURN

I2CSTOP
	CALL    NOACK
	call    BSTOP               ; Generate stop bit
	RETURN
    ;
I2CRSTART
	IORLW	H'01'
	MOVWF	i2caddr		; I2Cアドレス退避
	call    BSTART              ; Generate start bit
                                    ; Now send the control byte
                                    ; for a write, to set address
	movf    i2caddr,W
	movwf   datao               ; Copy control byte to buffer
	call    TX                  ; Output control byte to device
	RETURN
;*******************Start bit subroutine**************************
;           This routine generates a Start condition
;           (high-to-low transition of SDA while SCL
;           is still high.
;*****************************************************************
BSTART
    bsf     GPIO,SCL            ; Make sure SCL is high
    TSUSTA                      ; Start condition setup time delay
    BANKSEL	1
    BCF     TRISIO,SDA          ; Configure SDA to be an output
    BANKSEL	0
    bcf     GPIO,SDA            ; Pull SDA low to initiate
                                ;  Start condition
    THDSTA                      ; Start condition hold time delay
    bcf     GPIO,SCL            ; Bring SCL low in preparation
                                ;  for data transfer
    BANKSEL	1
    BSF     TRISIO,SDA          ; Configure SDA to be an input
    BANKSEL	0
    retlw   0

;*******************Stop bit subroutine***************************
;           This routine generates a Stop condition
;           (low-to-high transition of SDA while SCL
;           is still high.
;*****************************************************************
BSTOP
    BANKSEL	1
    BCF     TRISIO,SDA          ; Configure SDA to be an output
    BANKSEL	0

    bcf     GPIO,SDA            ; Pull SDA low
    bsf     GPIO,SCL            ; Make sure SCL is high
    TSUSTO                      ; Stop condition setup time delay
    BANKSEL	1
    BSF     TRISIO,SDA          ; Configure SDA to be an input
    BANKSEL	0
    retlw   0

;*******************Bit output subroutine*************************
;           This routine outputs bit 'DO' in 'buffer' to
;           the serial EEPROM device.
;*****************************************************************
BITOUT
    bcf     GPIO,SCL            ; Make sure SCL is low
    btfss   buffer,DO           ; Check for state of bit to xmit
    goto    bitlow              ; If bit is low, pull SDA low
clockout                        ; If bit is high, leave SDA high
    bsf     GPIO,SCL            ; Bring SCL high to begin transfer
    THIGH                       ; Clock high time delay
    bcf     GPIO,SCL            ; Bring SCL low again
    BANKSEL	1
    BSF     TRISIO,SDA          ; Configure SDA to be an input
    BANKSEL	0
    retlw   0

bitlow
    BANKSEL	1
    BCF     TRISIO,SDA          ; Configure SDA to be an output
    BANKSEL	0
    bcf     GPIO,SDA            ; Pull SDA low
    goto    clockout

;*******************Bit input subroutine**************************
;           This routine inputs a bit of data from the
;           serial EEPROM device, and stores it in bit
;           'DO' of 'buffer'.
;*****************************************************************
BITIN
    bcf     GPIO,SCL            ; Make sure SCL is low
    BANKSEL	1
    BSF     TRISIO,SDA          ; Configure SDA to be an input
    BANKSEL	0
    bsf     buffer,DI           ; Assume input bit is high
    bsf     GPIO,SCL            ; Bring SCL high to begin transfer
    TAA                         ; Output valid from clock delay
    btfss   GPIO,SDA            ; Check for state of SDA bit
    bcf     buffer,DI           ; If SDA is low, set bit low
    bcf     GPIO,SCL            ; Bring SCL low again
    retlw   0

;*******************Data transmit subroutine**********************
;           This routine transmits the byte of data
;           stored in 'datao' to the serial EEPROM
;           device. Instructions are also in place
;           to check for an ACK bit, if desired.
;           Just replace the 'goto' instruction,
;           or create an 'ackfailed' label, to provide
;           the functionality.
;*****************************************************************
TX
    movlw   .8
    movwf   bitcount            ; Initialize loop counter to 8

txlp
    bsf     buffer,DO           ; Assume output bit is high
    btfss   datao,7             ; Check state of next output bit
    bcf     buffer,DO           ; If low, set buffer bit low
    call    BITOUT              ; Call routine to output bit
    rlf     datao,F             ; Rotate datao left for next bit
    decfsz  bitcount,F          ; Decrement counter, check if done
    goto    txlp                ; Keep looping
    CALL    ACK
    retlw   0

;*******************Data receive subroutine***********************
;           This routine reads in one byte of data from
;           the serial EEPROM device, and stores it in
;           'datai'.  It then responds with either an
;           ACK or a NO ACK bit, depending on the value
;           of 'ACKB' in 'buffer'.
;*****************************************************************
RX
    clrf    datai               ; Clear input buffer
    movlw   .8
    movwf   bitcount            ; Initialize loop counter to 8
    bcf     STATUS,C            ; make sure carry bit is low
rxlp
    rlf     datai,F             ; Rotate datai 1 bit left
    call    BITIN               ; Read a bit
    btfsc   buffer,DI
    bsf     datai,0             ; Set bit 0 if necessary
    decfsz  bitcount,F          ; 8 bits done?
    goto    rxlp                ; If not, do another
    call    BITIN               ; Call routine to read ACK bit
    RETLW   0

ACK
    bcf     buffer,DO           ; If ACKB = 1, send ACK (DO = 0)
    call    BITOUT              ; Send bit
    retlw   0
NOACK
    bsf     buffer,DO           ; If ACKB = 1, send ACK (DO = 0)
    call    BITOUT              ; Send bit
    retlw   0
;
;---------------------------------------------------------------------
	END
;---------------------------------------------------------------------
;  終わり
;---------------------------------------------------------------------