ケースに開けた給電用のUSB Type-Cコネクタ穴にコネクタが合うように基板の取付穴を微調整。丸棒ヤスリで穴の形を加工。
2026年10月4日日曜日
LINEに通知する見守りセンサ工作再開
ケースに開けた給電用のUSB Type-Cコネクタ穴にコネクタが合うように基板の取付穴を微調整。丸棒ヤスリで穴の形を加工。
2026年10月2日金曜日
ギヤードステッピングモータ 28BYJ-48 を 2相バイポーラに改造
・ステッピングモーター 28BYJ-48 5V ULN2003ドライバーボード付き 2個650円
・DC 5V ステッピングモータ+ 28BYJ-48 ULN2003ドライバーボード 4個1,195円2026年9月27日日曜日
工作用工具を新調
久しぶりの工作ネタ。涼しくなってきて工作するのによい季節になりました。以前も書きましたが工作部屋にはエアコンが無いので、真夏や真冬の工作は厳しいものがあります。ということで工作を再開するにあたり、今回新たに購入した工作用の工具の紹介です。
まずは導電性マット。最近小さなビスや部品を机の上で見失うことが多く(机の上が乱雑ということもあるが、工作を始めると散らかる)、以前から導入しようかと思っていた。上に置いてある30cmの定規からサイズ感が分かるかと思いますが、幅45cmくらい 550×350mmの広いマットです。大き目の基板などを扱うことを考えてこれにしました。ちょっと匂うけど、そのうち揮発するでしょう。
2026年8月15日土曜日
2ch温度ロガー(デジタル温度センサ版)の回路図・スケッチ
夏休み、少し帰省していました。帰省してもやることは庭木の剪定、伐採、草むしり、除草剤撒きなど自宅と同じです。あとお墓参り。往路では大渋滞にはまり普段の2倍の時間がかかり、帰路では高速で台風一過の土砂降りに見舞われトホホです。
天気鍋は結構利用していますが、雲が多くて最高温度記録は更新できていません。最高温度に達する前にお昼のそうめんや冷や麦に使ってしまうのも一因ですが。今日は冷やし中華に利用。午前中、雲が多くて温度は60℃弱でした。
天気鍋の温度推移の記録に使っている2ch温度ロガーは昨年9月に作り始め、今年の2月には、回路図・スケッチをアップしていました(アップしたことを忘れていた)。ところが、温度センサの防水が破れて異常値を記録 するようになってしまったため、急遽 防水対応のデジタル温度プローブへの交換を行いました。最初からこれを使えばよかった。
以下、これまでの経緯です。
- 2ch温度ロガーの製作 2025年9月14日 日曜日
- 2ch温度ロガーの製作(2;MicroSDに記録)2025年9月16日火曜日
- 2ch温度ロガーの製作(3;温度センサ)2025年9月20日土曜日
- 2ch温度ロガーの製作(4;評価)2025年9月23日火曜日
- 2ch温度ロガーの製作(5;ケース加工)2025年9月27日土曜日
- 2ch温度ロガーの製作(6;ユニバーサル基板苦戦中)2025年9月28日日曜日
- 2ch温度ロガーの製作(7;トラブル解消)と自作ラジオ修理 2025年10月5日日曜日
- 2ch温度ロガーの製作(8;Sleep対応)2025年10月12日日曜日
- 2ch温度ロガーの製作(9;ケース組み込み)2025年11月29日土曜日
- 2ch温度ロガーの製作(10;評価)2026年1月24日土曜日
- 2ch温度ロガーの製作(11;校正)2026年1月25日日曜日
- 2ch温度ロガーの製作(12;完成)2026年2月3日火曜日
- 2ch温度ロガーの温度センサ交換 2026年7月30日木曜日
- 測定の正確な日時は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 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はブレッドボードのまま。効果が確認できたらユニバーサル基板に載せ替えるつもり。
また暑くて終日快晴が期待できる日があったら記録を取ってみます。普段は特に記録せずに、かみさんが天気鍋を普段使いする予定。
2026年7月30日木曜日
2ch温度ロガーの温度センサ交換
2026年7月26日日曜日
夏休みの自由研究:ソーラークッカー 天気鍋(トラブル発生)
太陽を追尾するターンテーブルの調整は、ぼほよくて12時間で180度回転しました。今日は朝のうち晴れたり曇ったりだったので、あまり期待しないでターンテーブルに載せた天気鍋の評価です。写真はまだ南東の方を向いています。これが徐々に南に向いていく。
結果、当初から鍋に入れていたセンサの温度はずっと異常(グラフ橙色)、もう一方も終盤になって異常値を記録するようになっていました。昼にそうめんを茹でるために鍋を回収したので今日の最高温度は66.7℃付近。十分熱くなっていてソーラークッカーはちゃんと働いています。
2026年7月25日土曜日
夏休みの自由研究:ソーラークッカー 天気鍋(太陽追尾ターンテーブル)
久しぶりにArduinoで電子工作、と言っても工作全体から占める割合は僅か。
天気鍋で80℃のお湯を沸かすことを目指して、太陽を追尾するターンテーブルを作ってみました。動力は昔アマゾンで買った小型のギヤ付きステッピングモータ、これをArduino nanoで動かします。タイヤはミニ四駆のものでホイールは3Dプリントしたもの。モータの取り付け部品も3Dプリント。
テーブルは家にあったべニア板を丸く切り出して、写真のような回転ベアリングを使います。これで水2.5L+鍋+透明バケツ+反射板を支えられるハズ。
太陽の動きに合わせてモータの回転速度を調整します。1時間で15度回転させればよいので、あらかじめ概算のパルス間隔を出しておきました。タイヤの直径、テーブルにタイヤが接する半径、ステップ数とギヤードモータの減速による回転数などから計算しました。
中心に分度器を置き、テーブルに接しないように差し金を渡して計測開始。
2026年5月27日水曜日
非接触スイッチ使ったキッチンの手元灯製作記録【動画】
ナレーションに「ゆっくり」ではない最近の合成音声を使ってみました。短い動画なのにナレーション入れるの地味に大変。毎週のようにアップしているYouTuberはいつ寝ているの?
2026年5月17日日曜日
断捨離は続くけどお宝は見つからない
東京都心で今年初めての真夏日を記録したようですね。午前中は久しぶりに町田の市街地に出かけたのですが 、すでに暑かった。
主目的ではなかったのですが、プラバンが欲しくて久しぶりにハンズに寄ったらマテリアルや工具などはなくなっていて、少しおしゃれな雑貨の店といった感じ。ハンズはカインズに買収されたのでカインズとの棲み分けか? それではと、ジョルナという若い子向けのファッションビルの地下にあって以前はプラモやミニ四駆、工作材料を売っていた店に行ったら、すでに無くなっていて、トレーディングカード屋になっていた。最後に世界堂という画材屋さんに向かったら、そこも無くなっていた。手を動かして何か作ろうという人は減ってきているんだろうね。ただ、まだユザワヤは残っている模様。
さて昨日、TOFセンサがステンレスシンクに反射してLED照明が勝手にオンオフした問題は一応解決しました。LED照明を一旦取り外して内部のTOFセンサを水平ではなく、若干傾きをつけて斜めに取り付け。これによりステンレスシンクでの反射光がセンサに届かなくなったはず。再びキッチンに取り付けましたが誤反応はありません。
ところでGWからのかみさんの探し物はまだ見つかりません。色々な場所を探すついでにパソコン関係の断捨離も進めています。今回発掘されたのは、各種ストレージと大量のケーブル類。
右下は、SDカードの親玉のような薄くて大きなメモリカード64MB、なんという名前だろう? 昔のトイカメラに入っていた。これ以外はノートパソコンのPCカードスロットに挿すストレージ。左上は1.8インチHDD 5GB、そのとなりは10MBのストレージ、右端は3.5インチHDD(容量不明)。下はコンパクトフラッシュを挿したPCカード。
前回お宝が発掘されて思い出したのですが、どこかに純金名刺もあるはず。その昔、仕事で田中貴金属さんの工場に伺ったときに頂いたものなので由緒正しい本物のはず。何も知らなかったのですが、普通に名刺交換して、会議を行い、退去する際に純金の名刺になったものをお土産として頂きました。
長らく忘れていたのですが、4~5年前に片づけをしていたときに見つけ、大事だから仕舞っておいたのですが、仕舞い場所を完全に忘れた。断捨離ついでに探しているけど見つからない。何かに紛れて捨ててしまった恐れもある。
純金名刺は金が0.5gか1gらしいので、今の価格だと13,000円か26,000円の価値があります。一時期3万円/g近いこともあった。
ということで、探し物と断捨離はまだしばらく続きそう。
2026年5月16日土曜日
TOFセンサ照明に伏兵あり
先週取り付けたTOFセンサにより非接触でオンオフできるLED照明ですが、思わぬ伏兵がいました。まぁ思いもよらないから伏兵なんですが。
それは炊飯器の「湯気」です。TOFセンサの真下ではないのですがキッチンカウンタには炊飯器が置いてあり、ご飯が炊けて炊飯器から湯気が出始めると、照明が勝手にオンオフを繰り返します。夕方気付きましたが朝もそうだと思う。センサのレーザ光が湯気に反射して認識している模様。
という訳で、照明の左右を入れ替えてTOFセンサを左側に持ってきました。また、少し手前に取り付けて手を奥まで伸ばさなくて済むように少し改善。かみさんから届きにくいという声があったので。照明の取り付け方の変更は簡単でしたが、棚の中に隠れている電気配線を見直すのに少し苦戦。
2026年5月9日土曜日
TOFセンサ近接スイッチ組み込みLED照明の取り付け
連休も残すところ後1日、12連休も過ぎてみればあっと言う間でした。 そんな中ようやく、TOFセンサ近接スイッチでオンオフするLED照明 をキッチンに取り付けました。
シンクの上にある棚の底面に付いていた古い蛍光灯照明を外すと、少し日焼けしたような感じだったので棚の底面や側面を塗装をし直すところから開始。写真はまだマスキングテープが貼ってあります。
点灯中。今までの蛍光灯よりかなり明るくて、iPhoneの絞り機能が効いて窓の外が暗く映っています。
- TOFセンサ近接スイッチでオンオフするLED照明(完成) 2026年3月28日 【回路図とソース】
- TOFセンサを使った非接触スイッチ、電源トラブル 2026年3月7日
- TOFセンサによる近接スイッチ WDT実装他 2026年3月4日
- TOFセンサによる近接スイッチのユニバーサル基板実装 2026年3月1日
- TOFセンサによる近接スイッチを照明機器へ組み込む検討 2026年2月28日
- TOFセンサとPICで作った近接スイッチプロトタイプ(動画) 2026年2月22日
- TOFセンサをPIC12F629で使う(動いた) 2026年2月20日
- TOFセンサをPIC12F629で使う(使えてない) 2026年2月15日
- PICプログラムをchatGPTに書いてもらったけど、、、 2026年2月14日
- TOFセンサ(測距センサ)を試してみた 2026年1月31日
2026年4月30日木曜日
1万円の超音波カッターを試す(動画)
12連休の2日目ですが、もう2日も経って残り10日か、と思うとあっという間に過ぎ去りそう。昨日今日は天気がイマイチですが、涼しいので草むしりや剪定などを行ってます。雑草の伸び方がヤバい。
前回書いた1万円の超音波カッター を実際に使ってみました。インターバルタイマ に使っているタカチのケースへの穴開けです。とても楽に切れていい感じ。ただ、溶けた樹脂が盛り上がるので寸法ギリギリは狙えない。少しマージンをみて最後はヤスリやアートナイフで整えた方がよい。
動画(2分半)を撮りましたが、刃先にピントが合わず加齢の進んだ手ばかりよく映っています(^_^;
さらに庭木の剪定もまだ残っているし、越冬のためにリビングに避難していた観葉植物を外に出すのと、伸びすぎた枝を切って挿し木にしたい。また、高圧洗浄機ケルヒャーで掃除したいところもある。
そんなこんなの連休になりそう。
2026年4月25日土曜日
1万円の超音波カッター
今日は久しぶりの休日出勤、明日も出勤。その代わり30日、1日に代休とって、さらに5月7日、8日に年次休暇で、4 月29日から5月10日まで怒涛の12連休。途中帰省するけどあとはノープラン。溜まっている工作を何とかしたい。
さて、いつものようにアリエクを彷徨っていたら、1万円弱で購入できる超音波カッターがあったのでポチってみた。昨日届いていたけど開ける暇がなく、先ほど開封。格好いい化粧箱に入っています。型名はFS-S1 Ultrasonic Cutting Knife というもの。昔の超音波カッターと比べるとよい感じのデザイン。USB-TypeC PD 15Vで動作。
2026年4月1日水曜日
LINEに通知する見守りセンサ、最終テスト継続中
日曜日から開始したLINE見守りセンサは、普段使っていない部屋に置いてあり、見守り継続中です。昨日はその部屋に入るのが遅れてしまい、15時間無反応が検出されてしまいました。うまく動いていることが確認できたのでOKです。
今日は、毎月1日の12:00に見守り継続中であることを知らせる通知機能が無事に動作して、メッセージが届きました。職場で昼休みに確認して小さくガッツポーズ。LINEのトーク画面に桜の花びらが舞っています。
あとは、外出(旅行)スイッチが押されたあと、帰宅反応があるまで見守りを保留する機能と、帰宅しなくても4日経過すると見守りが自動復帰する機能の確認をしていきます。自動復帰は4日後(24Hx4)にしていますが、自動復帰からさらに15時間反応がなかったときにメッセージが飛びます。計111時間後ですね。このあたりを確認し、一通りの検証が終わったらスケッチと回路図をアップするつもりです。
ちなみに、ESP32開発キットを使ったプロトタイプのスケッチは以下にあります。機能的にはほぼ同じ。
- LINEに通知する見守りセンサ(その2;プロトタイプ) (2025年8月24日)
早いところ仕上げないと1年がかりのプロジェクトになってしまう。
2026年3月29日日曜日
LINEに通知する見守りセンサの最終テスト開始
急に暖かくなってお花見日和でした。しかし相変わらず花粉症が酷いので、遠出は避けて近所で軽くお花見。
今朝、見守りセンサに最終プログラムを書き込み、テストしようとしたら動作中を示す緑色LEDが点灯しません。プログラムの不具合かと調べましたが明らかに光るはず。基板の裏からLED接続箇所をテスタで測ったら異常です。念のためLEDテスタで電圧をかけてみましたが点灯しません。ユニバーサル基板に繋いだのは新しいLEDだったと思うのですが、壊れていたようです。滅多にないです。
ということで朝からLEDを交換してようやく長時間のテスト開始。主に以下の内容を確認します。
- 15時間反応がないとLINEにメッセージが飛ぶ。15時間以内にセンサの前を横切るとカウンタをリセットする
- 外出(旅行)スイッチを押しても4日以上経過すると見守りを再開する。よって4日+15時間でメッセージが飛ぶ
- 毎月1日の12:00に見守り継続中である旨のメッセージが飛ぶ。
見守り開始時に飛んできたメッセージ。
2026年3月28日土曜日
TOFセンサ近接スイッチでオンオフするLED照明(完成)
TOFセンサを使った近接スイッチでLED照明のオンオフを行う工作もいよいよ大詰め。
先日届いた小型の電源モジュール にAC100Vを繋いで動作確認したら全然電圧がでません。壊れているのかと思い、もう1個調べても同じ。調べてみると購入したショップの商品説明には記載がなかったのですが、他のショップの説明欄を見ると、外付けに耐圧の大きなコンデンサ(物理的にも大きくなる)が必要なことが判りました。これでは小さく作れません。トホホです。
諦めて当初の計画通りトランスレス電源で行くことにしました。TOFセンサが発光するタイミングで電位が下がるため、モニタ用に点灯させている白色LEDがチラチラします。そこで白色LEDはやめて緑色にしたら若干目立たなくなったのでOKとしました。
写真はLED照明の端カバーに基板を収めた様子です。照明の拡散カバーの切り欠きはTOFセンサと干渉するためカットした跡です。
プログラム(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
;---------------------------------------------------------------------
; 終わり
;---------------------------------------------------------------------


















































