PC and PC troubles

10/21/2017

Google スプレッドシートでスクレーピングのトライアル1

Googleスプレッドシートでは、Excelよりも簡単にWebスクレーピングが出来るらしいので、調べて、どのように実現出来るのかをテストしてみた。

Spread
   














①B3のセルに日本気象協会10日間天気予報のURL値を一つ書き込み、C7のセルからB3のアドレスを参照し、スクレーピングしてみた。
セル"C7"には、「 =IMPORTHTML(B3,"table",1) 」 と書き込めば、直ぐにデータをインポートし表示してくれる。(図の左側のデータ表示部分)ここで、
「 =IMPORTHTML(B3,"table",1) 」として表示してくれるのは、最初の5日分なので、残りの5日分(6日目から10日目まで)を表示させるには、最初の5日分の書き込みの邪魔にならない所のセル(表示させる左上隅になるセル)に「=IMPORTHTML(B3,"table",2) 」と記入し、日本気象協会10日間天気予報のURLの2番目に書かれているテーブルを読込み、ここに書き込む必要がある。

しかし、僕がExcelで行ったように、任意の数のURLデータが順番に並んでいるような場合には、そのURLが書かれたアドレスを参照・読込し、スクレーピングでデータをインポートして欲しい。

②B3のアドレス番号(3行目、2列目)を参照する方法は・・・、
セル"K7"にその方法を記載して試してみた。

セル"K7"には、「=IMPORTHTML(INDIRECT(ADDRESS(3,2)),"table",1) 」 と書き込めば、直ぐにデータをインポートし表示してくれた。(図の右側のデータ表示部分)

ADDRESS(3,2) の表記は、B3のアドレスを数値で参照する。
これだけでは、アドレスを指定するだけなので、INDIRECT関数を使って、そのアドレスに書かれたデータを表示させるようにしている。

結果は図のとおりである。

どちらの場合でも同じデータが表示された。

10/19/2017

複数訪問予定地の天気予報(3時間と10日間を組合せ)表示

複数の離れた場所の訪問予定地がある際に、天気予報はどうなるのか調べる事があるが、何回も天気予報情報を開いて調べ、何枚もプリントし、それらを並べて漸く、自分の行動予定に伴う天気の今後の状況が把握できる。
もう少し効率的に調べて表示したいものだと思い、Excelのスクレーピング技術で表示するようにしてみた。当初は10日間天気予報のデータだけで纏めていたが、最初の3日間なら、3時間天気予報のデータを使う事が出来るので、3時間天気予報と10日間天気予報を組合せて表示させるようにしてみた。
310
マクロの内容は殆ど同じではあるが、3時間天気予報と10日間天気予報では、記載方法が少し違っている部分を変更してやる必要がある。(左図で表示した結果サンプルでは、データの幅が広くて画面からはみ出し、この画面では残り2日分が表示出来ていない。)

なお、場所に関するデータ(図のA~B列)の作成方法は、10日間天気予報と同じとした。3時間天気予報の部分は内部処理で補っている。

作成したマクロを以下に表示する。変更した部分は、10日間天気予報で作成したマクロと比較すれば、容易に分かると思うが、マクロの中に若干のコメントも記載したので、興味がある方には参考になると思う。

Webを読み取りExcel内に書き写した部分は、マクロの最後の部分で消去しているので、この部分をコメント表示にすれば、どのような書き写しが行われたかが分かり、参考になると思う。

尚、Excelシート内にマクロを実行するボタンを作成しておけば、便利だと思う。

《注記》 2017/10/20: マクロの記述に間違いがあり、3時間天気予報が地域別で更新されなかったのを修正した。
3104上図では最初の3日間天気予報の表示が全て同じになってしまっているが、左の図では地域別に違った状況になっている。




《マクロ》

Sub WeatherScraper2()
'3時間天気予報と10日間天気予測を組み合わせ表示するようにした(2017/10/19)
 Dim i As Long, j As Long, k As Long, imax As Long
 Dim URL As String, URLSet As String
 Dim URL10days As String, URL3hours As String
 Dim charsell As String, dadd As String, sadd As String
 Dim nsell As Long, hnsel As Long
'雨の際のセルの色を付ける変数 rain 雨⇒20 雨天以外は色無し⇒0
 Dim rain As Integer
'雨量が多く設定色番号 rainhard  33 を使うときの雨量(現在設定は 2㎜以上)
 Dim rainhard As Integer
'qrainstr は降水量セルに書かれた文字、qrain は降水量
 Dim qrainstr As String, qrainnbr As String, qrain As Long

 rainhard = 2
' i: URLとして書かれた処理入力番号
 i = 1

'URLが書かれたセル内にデータがある間処理を続ける。
 Do Until Cells(i + 2, 2) = ""
 
'10days.htmlから3hours.htmlを作る
 URL10days = Range(Cells(i + 2, 2).Address)
 URL3hours = Replace(URL10days, "10days", "3hours")

'先ず3時間天気予報のURLをセットし、スクレーピングする。
 URLSet = "URL;" & URL3hours

 With ActiveSheet.QueryTables.Add(Connection:= _
        URLSet, _
        Destination:=Range(Cells(101, 61 + (i - 1) * 9).Address))
        .Name = "?kd=1&tm=d&vl=a&mk=1&p=1"
        .FieldNames = True
        .RowNumbers = False
        .FillAdjacentFormulas = False
        .PreserveFormatting = True
        .RefreshOnFileOpen = False
        .BackgroundQuery = True
        .RefreshStyle = xlInsertDeleteCells
        .SavePassword = False
        .SaveData = True
        .AdjustColumnWidth = True
        .RefreshPeriod = 0
        .WebSelectionType = xlAllTables
        .WebFormatting = xlWebFormattingNone
        .WebPreFormattedTextToColumns = True
        .WebConsecutiveDelimitersAsOne = True
        .WebSingleBlockTextImport = False
        .WebDisableDateRecognition = False
        .WebDisableRedirections = False
        .Refresh BackgroundQuery:=False
 End With
 
'不要な4日目以降のデータ領域を削除する。
 Range(Cells(155, 61 + (i - 1) * 9), Cells(240, 70 + (i - 1) * 9).Address).Delete

'次に10日間天気予報のURLをセットし、スクレーピングする。
'この際、.RefreshStyle は xlOverwriteCells とする。
 URLSet = "URL;" & URL10days
 
 With ActiveSheet.QueryTables.Add(Connection:= _
        URLSet, _
        Destination:=Range(Cells(155, 61 + (i - 1) * 9).Address))
        .Name = "?kd=1&tm=d&vl=a&mk=1&p=1"
        .FieldNames = True
        .RowNumbers = False
        .FillAdjacentFormulas = False
        .PreserveFormatting = True
        .RefreshOnFileOpen = False
        .BackgroundQuery = True
        .RefreshStyle = xlOverwriteCells
        .SavePassword = False
        .SaveData = True
        .AdjustColumnWidth = True
        .RefreshPeriod = 0
        .WebSelectionType = xlAllTables
        .WebFormatting = xlWebFormattingNone
        .WebPreFormattedTextToColumns = True
        .WebConsecutiveDelimitersAsOne = True
        .WebSingleBlockTextImport = False
        .WebDisableDateRecognition = False
        .WebDisableRedirections = False
        .Refresh BackgroundQuery:=False
 End With

'不要なデータ領域を削除する。少し大きめの領域を削除
 Range(Cells(229, 61 + (i - 1) * 9), Cells(250, 70 + (i - 1) * 9).Address).Delete

'WebからExcelに書き写した表データ部分から、
'新たな表作成に必要なデータ部分をピックアップしてC列から右側に表記する。

 If i = 1 Then
'先ず、日・曜日を転記する。日と曜日のデータは、61列156行目から
    For j = 1 To 3
      Cells(1, 3 + (j - 1) * 8) = Cells(156 + (j - 1) * 7, 61)
    Next j
    
    For j = 4 To 5
      Cells(1, 27 + (j - 4) * 4) = Cells(177 + (j - 4) * 7, 61)
    Next j
    
    For j = 6 To 10
      Cells(1, 35 + (j - 6) * 4) = Cells(194 + (j - 6) * 7, 61)
    Next j
    
'時間枠(3:00,6:00,9:00,12:00,15:00,18:00,21:00,24:00)を記載する。
'これらの値は数値なので、シリアル値に換算するため24で割る。
    For j = 1 To 3
        For k = 1 To 8
          Cells(2, 3 + (k - 1) + (j - 1) * 8) = Cells(104, 62 + (k - 1)) / 24
        Next k
    Next j
    
'次に時間枠(3:00,9:00,15:00,21:00)を転記する。
'ここは文字列なので、其のまま転記すればよい。
    For j = 4 To 10
        For k = 1 To 4
          Cells(2, 27 + (k - 1) + (j - 4) * 4) = Cells(180 + (k - 1), 61)
        Next k
    Next j
  
'日付と時刻を見やすくするため、セルの書式設定、セルの結合を行う。
    For j = 1 To 3
      Cells(1, 3 + (j - 1) * 8).NumberFormatLocal = "G/標準"
      Range(Cells(1, 3 + (j - 1) * 8), Cells(1, 10 + (j - 1) * 8)).Merge
      Range(Cells(1, 3 + (j - 1) * 8), Cells(1, 10 + (j - 1) * 8)).HorizontalAlignment = xlCenter
        For k = 1 To 8
          Cells(2, 3 + (k - 1) + (j - 1) * 8).NumberFormatLocal = "[h]:mm"
        Next k
    Next j

    For j = 4 To 10
      Cells(1, 27 + (j - 4) * 4).NumberFormatLocal = "G/標準"
      Range(Cells(1, 27 + (j - 4) * 4), Cells(1, 30 + 4 * (j - 4))).Merge
      Range(Cells(1, 27 + (j - 4) * 4), Cells(1, 30 + 4 * (j - 4))).HorizontalAlignment = xlCenter
        For k = 1 To 4
          Cells(2, 27 + (k - 1) + (j - 4) * 4).NumberFormatLocal = "hh:mm"
        Next k
    Next j
End If

'天気に対するコメントを転記する。
     For j = 1 To 3
         For k = 1 To 8
           sadd = Range(Cells(107 + (j - 1) * 18, 62 + (k - 1)+ (i - 1) * 9).Address)
           If InStr(sadd, "晴れ") > 0 Then
           rain = 0
           Else
           End If
           If InStr(sadd, "曇り") > 0 Then
           rain = 15
           Else
           End If

           If InStr(sadd, "雨") > 0 Then
           rain = 20
'降水量が書き込まれたセル[qrainstr]
'文字を数値[qrain]にして、rainhard 以上なら青い色を濃い青 33 にする。
           qrainstr = Range(Cells(112 + (j - 1) * 18, 62 + (k - 1) + (i - 1) * 9).Address)
           qrain = Val(qrainstr)
             If qrain > 0 Then
'「小雨」で、雨量が 0以上なら色は薄い水色(28)に設定する。
             rain = 28
             Else
             End If
             
             If qrain >= rainhard Then
'「小雨」で、雨量がここで設定した2mm以上なら色は濃い水色(33)に設定する。
             rain = 33
             Else
             End If
         Else
         End If
         
'セルに天気コメントを書き入れ、そのセルの色を付ける。
         Cells(3 + (i - 1), 3 + (k - 1) + (j - 1) * 8) = sadd
         Cells(3 + (i - 1), 3 + (k - 1) + (j - 1) * 8).Interior.ColorIndex = rain
         Next k
     Next j
     
'天気に対するコメント、データは62列より(晴れ、曇りなど)を転記する。
'ここからは、URLを読んだ数毎に、読込み列は+9する必要がある。
'Webから写し取ったセル内の天気コメントは、全て二重表現になるので、文字数を1/2にする。
     For j = 4 To 5
         For k = 1 To 4
           charsell = Range(Cells(180 + (k - 1) + (j - 4) * 7, 62 + (i - 1) * 9).Address)
'セル内の文字数を数える。
           nsell = Len(charsell)
'二重に書いているので数値を半分にする。
           hnsel = nsell / 2
'セル枠に書かれている文字の前半分
           sadd = Mid(charsell, 1, hnsel)

           If InStr(sadd, "晴れ") > 0 Then
           rain = 0
           Else
           End If
           If InStr(sadd, "曇り") > 0 Then
           rain = 15
           Else
           End If

           If InStr(sadd, "雨") > 0 Then
           rain = 20
'降水量が書き込まれたセル[qrainstr]には、文字形式で「2㎜」などと書かれている。
'全角1文字[㎜]の左側の文字を数値[qrain]にして、rainhard 以上なら青い色を濃い青 33 にする。
           qrainstr = Range(Cells(180 + (k - 1) + (j - 4) * 7, 65 + (i - 1) * 9).Address)
           qrainnbr = Left(qrainstr, InStr(qrainstr, "㎜") - 1)
           qrain = Val(qrainnbr)
             If qrain > 0 Then
'「小雨」で、雨量が 0以上なら色は薄い水色(28)に設定する。
             rain = 28
             Else
             End If
             
             If qrain >= rainhard Then
'「小雨」で、雨量がここで設定した2mm以上なら色は濃い水色(33)に設定する。
             rain = 33
             Else
             End If
         Else
         End If
         
'セルに天気コメントを書き入れ、そのセルの色を付ける。
         Cells(3 + (i - 1), 27 + (k - 1) + (j - 4) * 4) = sadd
         Cells(3 + (i - 1), 27 + (k - 1) + (j - 4) * 4).Interior.ColorIndex = rain
         Next k
     Next j

'6日目以降の処理は、1~5日目までの処理と同じだが、転記元の場所が違うので別処理とした
     For j = 6 To 10
         For k = 1 To 4
           charsell = Range(Cells(197 + (k - 1) + (j - 6) * 7, 62 + (i - 1) * 9).Address)
           nsell = Len(charsell)
           hnsel = nsell / 2
           sadd = Mid(charsell, 1, hnsel)
        
           If InStr(sadd, "晴れ") > 0 Then
           rain = 0
           Else
           End If
           If InStr(sadd, "曇り") > 0 Then
           rain = 15
           Else
           End If
         
           If InStr(sadd, "雨") > 0 Then
           rain = 20
           qrainstr = Range(Cells(197 + (k - 1) + (j - 6) * 7, 65 + (i - 1) * 9).Address)
           qrainnbr = Left(qrainstr, InStr(qrainstr, "㎜") - 1)
           qrain = Val(qrainnbr)
             If qrain > 0 Then

             rain = 28
             Else
             End If
             
             If qrain >= rainhard Then
             rain = 33
             Else
             End If
         Else
     End If
        
'セルに天気コメントを書き入れ、そのセルの色を付ける。
         Cells(3 + (i - 1), 35 + (k - 1) + (j - 6) * 4) = sadd
         Cells(3 + (i - 1), 35 + (k - 1) + (j - 6) * 4).Interior.ColorIndex = rain
         Next k
     Next j

'B列のデータが有る限り処理する為カウンターを1つ進めて、最初の処理点に戻す。
 i = i + 1
Loop

imax = i - 1

'表の幅をデータの文字長さに合わせて自動調整する。
'範囲は、C列からBB列まで
Range("C:BB").Columns.AutoFit

'表の罫線を引く。
'罫線を引く範囲は、C1から第54列の第(imax+2)行まで
Range(Cells(1, 1), Cells(imax + 2, 54)).Borders.LineStyle = xlContinuous

'セルの中のデータは幅方向の中央に置く
Range(Cells(1, 3), Cells(imax + 2, 54)).HorizontalAlignment = xlCenter

'直ぐに必要としないデータ領域を非表示にし、Webから写し取ったセル部分を削除する。
Columns("B").Hidden = True
Range(Columns(61), Columns(61 + 6 + imax * 7)).Delete

End Sub

10/14/2017

Windows10 ユーザーアカウント制御されるアプリの自動起動方法

毎日定刻にPC(Windows10)を起動させ、その際に、アプリも自動起動させたい。ある設定した時刻になったら、PCをシャットダウンさせるという事をやりたかったのだが、ようやく上手く設定が出来たようなので、忘れないうちにここにメモをしておく。

僕が使っているPCの構成(monitorの仕様、BIOS設定方法など)の問題も有ったように思うので、そのシステムの概要をここに記載しておく。

PCの概要

 Motherboard: Z97N-WiFi  LGA1150用 Mini-iTX (Gigabyte製)
 CPU: intel Core i5 4590 @3.30GHz BOX(Haswell Refresh)
 Memory: CORSAIR VENGEANCE 16.0GB
 Graphics: 内臓IGFX
 HardDisk: PLEXTOR 
 PC Case: Cooler Master  Elite 130 Cube
 電源: Enermax ELT500AWT  500W仕様
 OS: Windows10 Pro 64bit版

周辺装置

 Monitor: LG FLATRON IPS206T-PN  (DVI接続)
 Keyboard: FILCO Excellio FKB109EXJB
 Mouse: Microsoft  Mobile Optical Mouse (有線)

調べてみると、自動起動はBIOSの「RTCによる電源ON」から行い、シャットダウンは「タスクスケジューラ」から行えば良いらしい。

問題点は、
1)PC起動時にDelキーを押しても、BIOS画面を開くことが出来なかったこと。
2)BIOS画面を開くことが出来るように変更後、PCを起動後に、監視カメラ用アプリが連動して上手く立ち上がらなかったこと。
3)監視カメラ用のアプリは立ち上がったものの、ユーザーアカウント制御のために、アプリがそこで止まってしまうこと。
上記の問題は、下記のように設定して、問題が解消し、監視カメラ用アプリは自動的に起動した。

1)の問題は、僕が使っている自作PCを製作した際に選択したレガッシーBIOSの問題かもしれないし、あるいは、使っているモニターのPCとの接続仕様によるものかもしれない。
しかし、BIOSが表示できないという問題の原因は、どちらなのかを切り分ける事が出来ていない。

1.BIOS画面を表示するために、
 いろいろやっている間に、PCの起動ディスク(C:ドライブ)に余計な書き込みをしてしまい、PCが起動できなくなった。しかし、このような問題は起きてしまうものなので、事前にHDD(D:)の中には起動用のバックアップを作り、DVDにも起動用ディスクを作っておいた。

作業場所の問題があり、周辺機器(モニター、キーボード、マウス)を外して、このPCだけを別の部屋に移動させて設定作業を行った。

 ①PCからSSDを取り外して、別のPCに組み入れ、ここでSSDのパーティションを外し、初期状態にした。
 ②初期状態に戻したSSDを、再び元のPCに組み込み、マザーボード上のCMOSジャンパーピンをショートさせて、データをクリアし、周辺機器を接続して、電源ON。
 ③これで、電源ON直後に「Del」キーを押すことで、BIOS画面に入ることができた。

2.BIOS画面での設定
 BIOS画面を開いて、システム電源を計画的にONにする設定を行う。
 ①Power Management (電力管理) 画面で、Resume by Alarmに入り、
  Wake up hour と Wake up minute を設定した。
  僕は、午前4時30分にPCを起動させるようにしたいので、hourの入力は「4」を、minuteの入力は「30」とした。

  毎日同じ時刻に起動なので、Wake up day は 「0」のままである。
  また、秒まで拘らないので、Wake up second も 「0」のままである。

 ②予期せぬ停電などで、AC電源が失われた場合の電源復帰もここで設定しておく。
  僕は、Memory を選択し、AC電源が戻った際に、システムが停電する前の状態と同じ状態となるようにした。
  
 ③キーボードやマウスによる電源ONは不要なので、Disabledを選択した。

 ④「Save & Exit」を選択し、保存して終了! これで、PCの起動設定は出来たはずだ。
  
 ⑤設定の確認のため、ここで行っている他の設定準備が終わり、しばらく時間を置いた後に、
 例えば、10数分後にこのPCが自動起動するように、前述のResume by Alarmの時刻設定を行って、この状態を「Save & Exit」で保存しておくことにする。

 この確認作業が終われば、再びBIOS画面で起動させる時間を設定し直す必要がある。
 忘れないようにしないと・・・。

3.Windows10のパスワードなし起動
 僕は、「Windows 10で起動時のパスワード入力を省略する方法
」のURLを参考にさせていただいた。

4.スタートアップにアプリを登録する
 PCが起動したときに連動して自動起動させたいアプリは、スタートアップに入れれば良いらしい。
 ①アプリをスタートアップに登録するためには、登録するアプリをデスクトップ画面に仮置きする。
 注記)このとき、アプリのプロパティを開き、
  「互換性」のタグをクリックして、一番下にある「管理者としてこのプログラムを実行する」にチェックを入れてはいけない。これを行うと、アプリが起動しなくなる可能性が高い。

 ②Windowsボタンをマウス右クリックし、「ファイル名を指定して実行」メニューを選択する。
 ③開いた窓の中の枠内に 「shell:startup」 と入力し、「OK」をクリックする。

 ④新たに、エクスプローラーのスタートアップフォルダーが表示される。

  これまでに、アプリのスタートアップ登録をしていなければ、このフォルダーの中は空のはずだ。

 ⑤このスタートアップフォルダーに、デスクトップに仮置きしておいたアプリを、ドラックアンドドロップで移動させる。

 ⑥PCを再起動させる。

 この作業が完了すれば、監視カメラ用アプリはPC起動直後に起動するだろうと思っていた。
 アプリは起動したが、起動後『ユーザーアカウント制御(UAC)』という機能が働いて、「このアプリがPCに変更を加えることを許可しますか?」と聞いてくる! ここで「はい」と答える必要がないようにする必要がある。そうしないと、シャットダウンの時刻までアプリは止まったままである。

 それほど単純ではなかった。
まだ他の項目を設定する必要がある。

この課題を解消するためには、

5.まず、タスク スケジューラでタスクを作成する必要がある。
タスクスケジューラの起動は、タスクバーの「検索フォーム」に「task」と打ち込むと、表示される筈。メニューから「タスク スケジューラ」を選んでクリックする。

①画面右側の操作メニューから「タスクの作成・・・」を選び、

②開いた「タスクの作成」の窓の 全般タグ に、名前、説明を記入する。
 僕は、名前に「MotionDetectCamera」と記載し、説明は空欄とした。
 ・ユーザーがログオンしているときのみ実行する・・・ボタンをON
 ・最上位の特権で実行する・・・・・・・・・・・・・にチェックを入れる。
 ・構成の枠内は、・・・・・・・・・・・・・・・・・Windows10を選択する。
 まだOKボタンはクリックしない。 

③操作タグ をクリックし、
 新規をクリックして新しい操作を作る。
 プログラムの開始を選んで、

 プログラム/スクリプト欄 には、
「参照」をクリックして、アプリが格納されている場所(例えば、C:\Program Files(x86)\UC\) からプログラム (UC.exe) を探し場所を確認する。
 そして「C:\Program」とだけ入力する。

 また、引数欄には、「Files(x86)\UC\UC.exe」と入力する。
 僕は、開始(オプション)欄に 「/rl highest」と入力した。

 OKをクリックして戻る。

④トリガータグ をクリックし、
 新規をクリック ⇒ 特定のユーザーのボタンを選択する。
 OKをクリックする。

⑤条件タグ をクリックし、
 入っているチェックをすべて外す。
 設定後、OKをクリックする。

注記)アプリケーションプログラム(ここでは”UC.exe”)は、ID、パスワードの入力無しで起動できるように設定しておくこと。

これでもまだ「最上位の特権で実行する」が選択されているが、未だ『ユーザーアカウント制御(UAC)』で止められてしまう。

6.そのためユーザーアカウント制御の設定を変更する
先ず、モニター画面左下隅タスクバーの「検索フォーム」に「uac」と入力して、

「ユーザーアカウント制御設定の変更」をクリックし、「
ユーザーアカウント制御の設定」窓画面で、4段階の3段階目に設定してOKをクリックする。


7.最後に、PCをシャットダウンする設定を行う。

先程4.で設定したが、タスク スケジューラで、シャットダウン用のタスクを作成する必要がある。
タスク スケジューラの起動は、タスクバーの「検索フォーム」に「task」と打ち込むと、表示される筈。メニューから「タスク スケジューラ」を選んでクリックし起動させる。

①操作のタブをクリックし、今度は「基本タスクの作成」をクリックする。

②名前は、「自動シャットダウン」と付け、次へのボタンをクリックした。

③次の画面タスクトリガーでは、毎日のボタンを選択し、次へをクリックし、
  毎日の 開始時刻を設定する。
    僕は、当日の深夜「23:55:00」に設定し、次へのボタンをクリック。

④操作の画面では、「プログラムの開始」ボタンを選択し、次へ。

  プログラム/スクリプトには、
   「 shutdown 」
  引数の追加には、
   「 /s /f 」
  と入力し、次へ。

⑤完了画面で、「完了」ボタンをクリックする。

以上で、全ての設定作業が完了したので、PCを再起動、又はシャットダウンした後PCの電源ボタンを押してみる・・・・・アプリは立ち上がったかな?

別のPCで確認した結果 この設定で無事立ち上がった!

アプリプログラムが上手く立ち上がらないので、ここでの設定に問題があるのかと思って色々調べた結果、アプリプログラムには、32bit版と64bit版の2種類があり、PCが64bit OSだったので64bit版のアプリを選んでインストールして確認しようとしていたが、何度やってもアプリの起動がおかしいので、32bit版にインストールし直して確認してみた。
電源ON後、ユーザーアカウント制御(UAC)も表示されずにパスし、アプリ(UC.exe)も起動し、画面表示された。

09/30/2017

Webの10日間天気予報をExcelに書き写し編集する

日本気象協会の10日間天気予報のWebページのデータを写し取り、複数の場所のURLデータを一覧にして、天気の状況の変化をみるための処理を行う。Webページの表現方法が変更になって、以前作成したマクロでは処理が出来なくなっていたので見直す事にした。しばらくぶりに見るマクロで、処理をどのように書いていたか、思い出すのに一苦労した。

データ処理のマクロは、下記のようになる。
Webデータを書き写す際の間違いに気づいたので、今回はここも修正した。

「日本気象協会の10日間天気予報」を活用した、以前僕が発表したマクロは、今回発表のマクロ(下記)に変更して欲しい。

《注記》下記マクロの第80,84行目にCell位置指示の間違いが有ったので修正した。(2017/10/04)

《マクロ部分》

Sub WeatherScraper()
'URL内の記載状況が変わったため、マクロを見直し新バージョンとして作成(2017/09/28)
 Dim i As Long, j As Long, k As Long, imax As Long
 Dim URL As String, URLSet As String
 Dim charsell As String, dadd As String, sadd As String
 Dim nsell As Long, hnsel As Long
'雨の際のセルの色を付けるため 雨⇒20 雨天以外は色無し⇒0
 Dim rain As Integer
'雨量が多く設定色番号 33 を使うときの雨量(現在設定は 2㎜以上)
 Dim rainhard As Integer
'qrainstr は降水量セルに書かれた文字、qrain は降水量
 Dim qrainstr As String, qrainnbr As String, qrain As Long

'多降水量設定にする数値 僕は2mmとした。
 rainhard = 2
' i: URLとして書かれた処理入力番号
 i = 1

'URLが書かれたセル内にデータがある間処理を続ける。
 Do Until Cells(i + 2, 2) = ""

'URLの読み込み位置にデータが存在しているので、天気予報のURLをセットする。
 URLSet = "URL;" & Range(Cells(i + 2, 2).Address)

'WebからExcelシート内にデータを書き写す位置の記載方法が間違っていたので
'Destinationの中の表現を修正した。
 With ActiveSheet.QueryTables.Add(Connection:= _
        URLSet, _
        Destination:=Range(Cells(101, 53 + (i - 1) * 7).Address))
        .Name = "?kd=1&tm=d&vl=a&mk=1&p=1"
        .FieldNames = True
        .RowNumbers = False
        .FillAdjacentFormulas = False
        .PreserveFormatting = True
        .RefreshOnFileOpen = False
        .BackgroundQuery = True
        .RefreshStyle = xlInsertDeleteCells
        .SavePassword = False
        .SaveData = True
        .AdjustColumnWidth = True
        .RefreshPeriod = 0
        .WebSelectionType = xlAllTables
        .WebFormatting = xlWebFormattingNone
        .WebPreFormattedTextToColumns = True
        .WebConsecutiveDelimitersAsOne = True
        .WebSingleBlockTextImport = False
        .WebDisableDateRecognition = False
        .WebDisableRedirections = False
        .Refresh BackgroundQuery:=False
 End With

'WebからExcelに書き写した表データ部分から、
'新たな表作成に必要なデータ部分をピックアップしてC列から右側に表記する。
 If i = 1 Then
'先ず、日・曜日を転記する。日と曜日のデータは、"BA"列に書き写してあり、
' 102,109,116,123,130,  140,147,154,161,168に記載されている。
' これを、列 3,7,11,・・・ となるので ⇒ 3+(J-1)*4 に転記する。
    For j = 1 To 5
      Cells(1, 3 + (j - 1) * 4) = Cells(102 + (j - 1) * 7, "BA")
    Next j
    
    For j = 6 To 10
      Cells(1, 3 + (j - 1) * 4) = Cells(102 + 3 + (j - 1) * 7, "BA")
    Next j

'次に時間枠(3:00,9:00,15:00,21:00)を転記する。
    For j = 1 To 5
        For k = 1 To 4
          Cells(2, 3 + (k - 1) + (j - 1) * 4) = Cells(105 + (k - 1) + (j - 1) * 7, "BA")
        Next k
    Next j
    For j = 6 To 10
        For k = 1 To 4
          Cells(2, 3 + (k - 1) + (j - 1) * 4) = Cells(105 + 3 + (k - 1) + (j - 1) * 7, "BA")
        Next k
    Next j
  
'日付と時刻を見やすくするため、セルの書式、セルの結合をしておく。
    For j = 1 To 10
      Cells(1, 3 + 4 * (j - 1)).NumberFormatLocal = "G/標準"
      Range(Cells(1, 3 + 4 * (j - 1)), Cells(1, 6 + 4 * (j - 1))).Merge
      Range(Cells(1, 3 + 4 * (j - 1)), Cells(1, 6 + 4 * (j - 1))).HorizontalAlignment = xlCenter
        For k = 1 To 4
          Cells(2, 3 + (k - 1) + 4 * (j - 1)).NumberFormatLocal = "hh:mm"
        Next k
    Next j
End If

'天気に対するコメント、最初のデータはBB列(54列)より(晴れ、曇りなど)を転記する。
'ここからは、URLを読んだ数毎に、読込み列は+7する必要がある。
'Webから写し取ったセル内の天気コメントは、全て二重表現になるので、文字数を1/2にする。
     For j = 1 To 5
         For k = 1 To 4
           charsell = Range(Cells(105 + (k - 1) + (j - 1) * 7, 54 + (i - 1) * 7).Address)
'セル内の文字数を数える。
           nsell = Len(charsell)
'二重に書いているので数値を半分にする。
           hnsel = nsell / 2
'セル枠に書かれている文字の前半分
           sadd = Mid(charsell, 1, hnsel)

           If InStr(sadd, "晴れ") > 0 Then
           rain = 0
           Else
           End If
           If InStr(sadd, "曇り") > 0 Then
           rain = 15
           Else
           End If

           If InStr(sadd, "雨") > 0 Then
           rain = 20
'降水量が書き込まれたセル[qrainstr]には、文字形式で「2㎜」などと書かれている。
'全角1文字[㎜]の左側の文字を数値[qrain]にして、rainhard 以上なら青い色を濃い青 33 にする。
           qrainstr = Range(Cells(105 + (k - 1) + (j - 1) * 7, 57 + (i - 1) * 7).Address)
           qrainnbr = Left(qrainstr, InStr(qrainstr, "㎜") - 1)
           qrain = Val(qrainnbr)
             If qrain > 0 Then
'「小雨」で、雨量が 0以上なら色は薄い水色(28)に設定する。
             rain = 28
             Else
             End If
             
             If qrain >= rainhard Then
'「小雨」で、雨量がここで設定した2mm以上なら色は濃い水色(33)に設定する。
             rain = 33
             Else
             End If
         Else
         End If
         
'セルに天気コメントを書き入れ、そのセルの色を付ける。
         Cells(3 + (i - 1), 3 + (k - 1) + (j - 1) * 4) = sadd
         Cells(3 + (i - 1), 3 + (k - 1) + (j - 1) * 4).Interior.ColorIndex = rain
         Next k
     Next j

'基本的に1~5日目までの処理と同じだが、記載されている場所が違うので別処理とした
     For j = 6 To 10
         For k = 1 To 4
           charsell = Range(Cells(105 + 3 + (k - 1) + (j - 1) * 7, 54 + (i - 1) * 7).Address)
           nsell = Len(charsell)
           hnsel = nsell / 2
           sadd = Mid(charsell, 1, hnsel)
        
           If InStr(sadd, "晴れ") > 0 Then
           rain = 0
           Else
           End If
           If InStr(sadd, "曇り") > 0 Then
           rain = 15
           Else
           End If
         
           If InStr(sadd, "雨") > 0 Then
           rain = 20
           qrainstr = Range(Cells(105 + 3 + (k - 1) + (j - 1) * 7, 57 + (i - 1) * 7).Address)
           qrainnbr = Left(qrainstr, InStr(qrainstr, "㎜") - 1)
           qrain = Val(qrainnbr)
             If qrain > 0 Then

             rain = 28
             Else
             End If
             
             If qrain >= rainhard Then
             rain = 33
             Else
             End If
         Else
         End If
        
'セルに天気コメントを書き入れ、そのセルの色を付ける。
         Cells(3 + (i - 1), 3 + (k - 1) + (j - 1) * 4) = sadd
         Cells(3 + (i - 1), 3 + (k - 1) + (j - 1) * 4).Interior.ColorIndex = rain
         Next k
     Next j

'B列のデータが有る限り処理する為カウンターを1つ進めて、最初の処理点に戻す。
 i = i + 1
Loop

imax = i - 1

'直ぐに必要としないデータ領域を非表示にし、Webから写し取ったセル部分を削除する。
 Columns("B").Hidden = True
 Range(Columns(53), Columns(53 + imax * 7)).Delete

End Sub

《注記》 ここで、もしWebから転記した生データを確認したいのなら、このマクロの「Range(Columns(53), Columns(53 + imax * 7)).Delete」部分をコメント行にすればよい。そうすれば、第53列目から後半の列に、転記されたデータを見る事が出来る。

《Excelシートの書き方》

ExceldataExcelシートの書き方の事例を記載する。

①先ず、Google検索で
日本気象協会 10日間天気予報 青森県」をキーワードにして、該当するURLを検索する。
②検索された一覧より、
『青森市の10日間天気(6時間ごと) - 日本気象協会 tenki.jp』 を選ぶ。ここでのポイントは、(6時間ごと)というキーワードが入っているものを選ぶこと。
③②で選んだ画面を開き、URLをコピーする。
 この例では、『https://tenki.jp/forecast/2/5/3110/2201/10days.html
』となっている。
 10日間天気予報のデータが含まれているという事を htmlの直前に書かれている 
10days で確認しておく。
④これを、ExcelシートのB列に貼り付ける。
 貼り付ける順番は、自分が訪問したい場所順に、第1番目の訪問場所のURLは3行目に、第2番目の訪問場所は4行目に記載、・・・。必要な訪問場所を順番に貼り付ける。
⑤天気予報を確認したい場所のURL貼り付けが終われば準備完了である。
 マクロは、B列にデータが無くなれば処理を終了するようになっている。

 ・C列からAP列に処理結果が記載され、WebからこのExcelシート内に転記したデータは消去するようにしてある。
 ・
C列からAP列のセル幅は、標準値になっているので、見やすくするために列幅の自動調整をするのが良い。

⑥A列の第3行目以降は、URLの説明書きとして使用するのが良い。
 また、AQ列~AZ列もコメント書きに使用することが出来る。

⑦マクロを実行させた上記のデータでの結果(2017/10/01 23:00処理)は、下記のようになった。(ご参考)
10










《表示結果をより見易くするために》
 2017/10/04追記

①表の幅をデータの文字長さに合わせて、C列からAP列までを自動調整する。
表のC1から第42列(AP列)の第(imax+2)行まで罫線を引く。
③②の
セル中のデータは、セルの幅方向の中央に置く。
これを行うためには、上記マクロの第184行のところに、下記のマクロを挿入すればいい。

'表の幅をデータの文字長さに合わせて自動調整する。
'範囲は、C列からAP列まで
Range("C:AP").Columns.AutoFit

'表の罫線を引く。
'罫線を引く範囲は、C1から第42列(AP列)の第(imax+2)行まで
Range(Cells(1, 3), Cells(imax + 2, 42)).Borders.LineStyle = xlContinuous

'セルの中のデータは幅方向の中央に置く
Range(Cells(1, 3), Cells(imax + 2, 42)).HorizontalAlignment = xlCenter

07/23/2017

Arduino を使ってexercise の音楽と時間管理をしたい

Aruduinoを使ってエクササイズの音楽と時間管理をしてみたい。
先ずは、Arduinoで時間管理が出来るのか・・・試してみなければ・・・。

《リモコンで、
こんなことがしてみたい
①いわゆるラジカセの電源ON/OFF
②音楽の演奏順や演奏番号を指定
③ボリュームの増減や音量の指定

僕が現在想定しているラジカセは「東芝製 SD/USB/CDラジオ TY-CRX71(W)」で、メディアはSDカードを考えている。

この機種は、SDカードを挿入しておいて本体の電源を入れても、SDカードが認識されないので、確認不十分だが、リモコンで出来る操作はOFFのみかもしれない。
リモコンによるプログラムの手順などを確認する必要がある。
リモコンにある音量ボタンは「-/+」だけなので、Arduinoでどのようにすれば音量を変えられるか、確認する必要がある。

以上のような、確認不十分な課題があるが、とにかくArduinoで時間管理できれば、いわゆるラジカセは別のものに変更して、よりコントロールの行いやすいものにするという方法もあるだろう。

Img_20170723_171122 それで、Arduinoを使って時間管理するスケッチを作ってみることにした。

時間制御関数を調べてみると、Arduinoにはミリ秒やマイクロ秒で制御できる関数がある。この関数を使えば、開始時間からの制御が出来る。
時間関数については、Arduinoがスタートしてからの時刻のずれが問題にならないようにするため、

 millis() :Arduinoが起動してからの時間(ミリ秒)を返す

を使うことにした。また、時間をチェックするところは、ユーザー関数を作成した。

次に、エクササイズの決められた実行時間(1分間)と休憩時間(1分間)を、ほぼ正確に繰り返し(エクササイズ8回と休憩7回後、5分間の休憩を)制御するようにするため、繰り返しした際の条件判定・制御文をどうすれば良いか、随分迷った。
これに関連する制御文は、
 ① (if と) for を組み合わせた制御文
   for(初期処理;条件;変更処理){処理群}

 ② while 制御文
   while(条件){処理群}

 ③ do-while 制御文
   do{処理群}while(条件)

僕は、上記の制御文を試してみて、エクササイズの繰り返し判定には①を、時間の判定には③を用いて下記のようなスケッチを作成した。

リモコンの制御部分は、ここでは print文としてスケッチを作成してある。後で制御コードを調べた後に組み替えたい。

処理速度が速くないArduinoなので、Arduinoは2台の構成を考えている。
・ 1台は親機で、時間管理とラジカセの添付リモコンに代り、リモコンデータを送信する。
・ もう一台は、子機で、親機からの別のリモコンデータを受信して、MP3データで作成された音声を発音する。これは、以前のブログで紹介した記事「TVリモコンを使ってArduinoを喋らせる」を参考に作ることになると思う。

これらの詳細は、次回以降明らかに出来ると思う。ここでは、自分用のメモとして、下記Sketchをここに書いておく。

《時間管理のSketch案》

// Arduino を使った時間管理のための sketch
// 音量位置 Vol1:min, Vol2:med, Vol3:max
//
long i=0;
int  n=0;
int  m=0;
unsigned long atime;  // 告知時刻
unsigned long    tc;  //
unsigned long   tc2;  //
unsigned long t0 = millis();  // 時刻の初期化

// セットアップ
void setup(){
  Serial.begin(9600);
// CD  Playerへの指示
  Serial.println("Player の 電源ON");
  Serial.println("Player の 音楽を選択する");
  Serial.println("Player の Play!");
  
// CD  Playerへの指示
  Serial.println("Player の 音量 Vol1 へ");
  
  atime =15;
  
// ここから カウンター m=0 から 3回ループする
for (m=0; m<3; m++){
  Serial.println(" ");
  Serial.print("m = ");
  Serial.println(m+1);
  
// MP3 Playerへの指示:15秒前と告知する
  Serial.print("atime=");
  Serial.println(atime);
  CountTimer( atime);
  Serial.println(" ");
  Serial.println("スタート15秒前です!");

  atime = atime + 5;  // 5秒後に告知
  Serial.print("atime=");
  Serial.println(atime);
  CountTimer( atime);
  Serial.println(" ");
  Serial.println("10秒前です!");
  
// ここから カウンター n=0 から 8回ループする *****基本の運動ループ部分*****
for (n=0; n<8; n++ ){
// CD  Playerへの指示
  Serial.println("Player の 音量を Vol1 へ");
  
  Serial.print("n = ");
  Serial.println(n);
  
  atime = atime + 5;  // 5秒後に告知
  Serial.print("atime=");
  Serial.println(atime);
  CountTimer( atime);
  Serial.println(" ");
  Serial.println(" 5秒前です!");
  
  atime = atime + 2;  // 2秒後に告知
  Serial.print("atime=");
  Serial.println(atime);
  CountTimer( atime);
  tone(8, 440);
  Serial.println(" ");
  Serial.println(" 3秒前です!");
  noTone(8);
  
  atime = atime + 1;  // 1秒後に告知
  Serial.print("atime=");
  Serial.println(atime);
  CountTimer( atime);
  tone(8, 440);
  Serial.println(" ");
  Serial.println(" 2秒前です!");
  noTone(8);
  
  atime = atime + 1;  // 1秒後に告知
  Serial.print("atime=");
  Serial.println(atime);
  CountTimer( atime);
  tone(8, 440);
  Serial.println(" ");
  Serial.println(" 1秒前です!");
  noTone(8);
  
  atime = atime + 1;  // 1秒後に告知
  Serial.print("atime=");
  Serial.println(atime);
  CountTimer( atime);
  tone(8, 880);
  Serial.println(" ");
  Serial.print(n+1);
  Serial.println(" 回目の 1分間エクササイズ Go!!!!!!!!!!!!!!");
  noTone(8);
// CD  Playerへの指示
  Serial.println("Player の 音量を Vol3 へ");
  
  atime = atime + 55;  // 55秒後に告知
  CountTimer( atime);
// CD  Playerへの指示
  Serial.println(" ");
  Serial.println("Player の 音量を Vol1 へ");

  atime = atime + 2;  // 2秒後に告知
  Serial.print("atime=");
  Serial.println(atime);
  CountTimer( atime);
  tone(8, 440);
  Serial.println(" ");
  Serial.println(" 3秒前です!");
  noTone(8);

  atime = atime + 1;  // 1秒後に告知
  Serial.print("atime=");
  Serial.println(atime);
  CountTimer( atime);
  tone(8, 440);
  Serial.println(" ");
  Serial.println(" 2秒前です!");
  noTone(8);
  
  atime = atime + 1;  // 1秒後に告知
  Serial.print("atime=");
  Serial.println(atime);
  CountTimer( atime);
  tone(8, 440);
  Serial.println(" ");
  Serial.println(" 1秒前です!");
  noTone(8);
  
  atime = atime + 1;  // 1秒後に告知
  Serial.print("atime=");
  Serial.println(atime);
  CountTimer( atime);
  tone(8, 880);
  Serial.println(" ");
  Serial.print(n+1);
  Serial.println(" 回目の 1分間 Brake!!!!!!!!!!!!!!!!!!!!!!!");
  noTone(8);
// CD  Playerへの指示
  Serial.println("Player の 音楽を変更する");
  
  atime = atime + 50;  // 50秒後に告知
}
// 5分間休憩の時間をセット
  atime = atime - 50;
  atime = atime + 5*60 - 15;  // 4分45秒後に告知
  Serial.println(" ");
  Serial.print(m+1);
  Serial.println(" 回目の 5分間 Brake!!!!!!!!!!!!!!!!!!!!!!!");
// CD  Playerへの指示
  Serial.println("Player の 音量を Vol2 へ");
}
// CD  Playerへの指示
  Serial.println("Player をストップさせる");
  Serial.println("Player の電源 OFF");
}
void loop() { }
int CountTimer(unsigned long tsec)
{ // 時間をカウントチェックするためのユーザー関数
  // t0   : 正となる時刻
  // tsec : このサブプログラムで秒読みする時間

  int i=0;  // millis() を読むカウンター
  unsigned long tc;  // tc  : 現時刻
  unsigned long t2 = tsec*1000;    // t2  : t0からの tsec秒経過した時刻

  tc = millis();
  
  while( tc < t2 ){
  tc = millis();
  i=i+1;
  }
 return;
}

07/18/2017

Arduino で Sonic Radar を作る

Img_20170629_220931 Arduinoと超音波距離センサー「HC-SR04」(秋月電子から購入) を使った電子工作の中に面白いものを見つけたので、作ってみた。

これは、「Sonic Radar」というもので、デバイスから超音波を発して、跳ね返ってきた音を受信し、その時間で距離を計測するというものである。PCの助けを借りると、モニター画面上に映し出された表示結果は、レーダー画像を見ているような雰囲気がある。
参考にさせて戴いたURLは、
「ROBOTIC
TUTORIALS.COM」の中の、Ultrasonic based RADAR である。

ハードウェアの組立は、結線箇所も少なく簡単に出来る。ハードウェアの組立に関しては、多くの他のブログでも紹介記事があるので、参考にするとよい。
しかし、PCの画面に結果を表示するためには、Processingというソフトウェアの助けを借りなければならない。
僕は、Processingに関する知識が今は無いので、ネット上にあるものを拝借して出来るものをいろいろ探し回ってみた。私の組上げたシステムで表示できるもは、なかなか見つからなかった。ようやく見つけたのが、このURLである。どちらのSketch も殆どオリジナルのままである。しかし、表示部分に文字の重なりなどの問題があったので、フォントの大きさを変えたり、表示位置を変更し使わせて戴いている。
(注記:Sketch の中で init pin と言っているのは Trig Pin の事である。 まずArduinoを起動してから、次に Processing を起動してやれば良い。Processing が起動したとき、Arduino側の動きが一瞬停まるが、直ぐに同期して、PCの画面上に画像を映し出してくれる。)

《 Arduino Sketch 》

#include 
/*
code for arduino bord ultrasonic radar system.
*/            
Servo leftRightServo;         // set a variable to map the servo
int leftRightPos = 0;         // set a variable to store the servo position
const int numReadings = 10;   // set a variable for the number of readings to take
int index = 0;                // the index of the current reading
int total = 0;                // the total of all readings
int average = 0;              // the average
int echoPin = 6;              // the HC-SR04's echo pin
int initPin = 7;              // the HC-SR04's Trig pin
unsigned long pulseTime = 0;  // variable for reading the pulse
unsigned long distance = 0;   // variable for storing distance
/* setup the pins, servo and serial port */
void setup() {
  leftRightServo.attach(9);
  // make the init pin an output:
  pinMode(initPin, OUTPUT);
  // make the echo pin an input:
  pinMode(echoPin, INPUT);
  // initialize the serial port:
  Serial.begin(9600);
}  
/* begin rotating the servo and getting sensor values */
void loop() {
  for(leftRightPos = 0; leftRightPos < 180; leftRightPos++) {  // going left to right.
    leftRightServo.write(leftRightPos);
      for (index = 0; index <=numReadings;index++) {  // take x number of readings from the sensor and average them
        digitalWrite(initPin, LOW);
        delayMicroseconds(50);
        digitalWrite(initPin, HIGH);          // send signal
        delayMicroseconds(50);                // wait 50 microseconds for it to return
        digitalWrite(initPin, LOW);           // close signal
        pulseTime = pulseIn(echoPin, HIGH);   // calculate time for signal to return
        distance = pulseTime/58;              // convert to centimetres
        total = total + distance;             // update total

        delay(10);
      }
    average = total/numReadings;    // create average reading
 
    if (index >= numReadings)  {    // reset the counts when at the last item of the array
      index = 0;
      total = 0;
    }
    Serial.print("X");              // print leading X to mark the following value as degrees
    Serial.print(leftRightPos);     // current servo position
    Serial.print("V");              // preceeding character to separate values
    Serial.println(average);        // average of sensor readings
  }
  /*
  start going right to left after we got to 180 degrees
  same code as above
  */
  for(leftRightPos = 180; leftRightPos > 0; leftRightPos--) {  // going right to left
    leftRightServo.write(leftRightPos);
    for (index = 0; index <=numReadings;index++) {
      digitalWrite(initPin, LOW);
      delayMicroseconds(50);
      digitalWrite(initPin, HIGH);
      delayMicroseconds(50);
      digitalWrite(initPin, LOW);
      pulseTime = pulseIn(echoPin, HIGH);
      distance = pulseTime/58;
      total = total + distance;
      delay(10);
    }
    average = total/numReadings;
    if (index >= numReadings)  {
      index = 0;
      total = 0;
    }
    Serial.print("X");
    Serial.print(leftRightPos);
    Serial.print("V");
    Serial.println(average);
   }
}

《 Processing 用のSketch》

/*
Radar Screen Visualisation for HC-SR04
 Maps out an area of what the HC-SR04 sees from a top down view.
 Takes and displays 2 readings, one left to right and one right to left.
 Displays an average of the 2 readings
 Displays motion alert if there is a large difference between the 2 values.
 */
import processing.serial.*;   // import serial library
Serial arduinoport;           // declare a serial port
float x, y;              // variable to store x and y co-ordinates for vertices   
int radius = 350;        // set the radius of objects
int w = 300;             // set an arbitary width value
int degree = 0;          // servo position in degrees
int value = 0;           // value from sensor
int motion = 0;          // value to store which way the servo is panning
int[] newValue = new int[181];  // create an array to store each new sensor value for each servo position
int[] oldValue = new int[181];  // create an array to store the previous values.
PFont myFont;                   // setup fonts in Processing
int radarDist = 0;       // set value to configure Radar distance labels
int firstRun = 0;        // value to ignore triggering motion on the first 2 servo sweeps

/* create background and serial buffer */
void setup() {
  // setup the background size, colour and font.
  size(750, 450);
  background (0); // 0 = black
  myFont = createFont("verdana", 12);
  textFont(myFont);
  // setup the serial port and buffer
  arduinoport = new Serial(this, Serial.list()[0], 9600);
}

/* draw the screen */
void draw() {
  fill(0);                     // set the following shapes to be black
  noStroke();                  // set the following shapes to have no outline
  ellipse(radius, radius, 750, 750);  // draw a circle with a width/ height = 750 with its center position (x and y) set by the radius
  rectMode(CENTER);            // set the following rectangle to be drawn around its center
  rect(350, 402, 800, 100);    // draw rectangle (x, y, width, height)
  if (degree >= 179) {         // if at the far right then set motion = 1/ true we're about to go right to left
    motion = 1;                // this changes the animation to run right to left
  }
  if (degree <= 1) {           // if servo at 0 degrees then we're about to go left to right
    motion = 0;                // this sets the animation to run left to right
  }
  /* setup the radar sweep */
  /* 
   We use trigonmetry to create points around a circle.
   So the radius plus the cosine of the servo position converted to radians
   Since radians 0 start at 90 degrees we add 180 to make it start from the left
   Adding +1 (i) each time through the loops to move 1 degree matching the one degree of servo movement
   cos is for the x left to right value and sin calculates the y value
   since its a circle we plot our lines and vertices around the start point for everything will always be the center.
   */
  strokeWeight(7);             // set the thickness of the lines
  if (motion == 0) {           // if going left to right
    for (int i = 0; i <= 20; i++) {  // draw 20 lines with fading colour each 1 degree further round than the last
      stroke(0, (10*i), 0);    // set the stroke colour (Red, Green, Blue) base it on the the value of i
      line(radius, radius, radius + cos(radians(degree+(180+i)))*w, radius + sin(radians(degree+(180+i)))*w); // line(start x, start y, end x, end y)
    }
  } else {                     // if going right to left
    for (int i = 20; i >= 0; i--) {  // draw 20 lines with fading colour
      stroke(0, 200-(10*i), 0);      // using standard RGB values, each between 0 and 255
      line(radius, radius, radius + cos(radians(degree+(180+i)))*w, radius + sin(radians(degree+(180+i)))*w);
    }
  }
  /* Setup the shapes made from the sensor values */
  noStroke();                  // no outline
  /* first sweep */
  fill(0, 50, 250);            // set the fill colour of the shape (Red, Green, Blue)
  beginShape();                // start drawing shape
  for (int i = 0; i < 180; i++) {    // for each degree in the array
    x = radius + cos(radians((180+i)))*((oldValue[i])); // create x coordinate
    y = radius + sin(radians((180+i)))*((oldValue[i])); // create y coordinate
    vertex(x, y);              // plot vertices
  }
  endShape();                  // end shape
  /* second sweep */
  fill(0, 110, 0);
  beginShape();
  for (int i = 0; i < 180; i++) {
    x = radius + cos(radians((180+i)))*(newValue[i]);
    y = radius + sin(radians((180+i)))*(newValue[i]);
    vertex(x, y);
  }
  endShape();
  /* average */
  fill(0, 170, 0);
  beginShape();
  for (int i = 0; i < 180; i++) {
    x = radius + cos(radians((180+i)))*((newValue[i]+oldValue[i])/2); // create average
    y = radius + sin(radians((180+i)))*((newValue[i]+oldValue[i])/2);
    vertex(x, y);
  }
  endShape();
  /* if after first 2 sweeps, highlight motion with red circle*/
  if (firstRun >= 360) {
    stroke(150, 0, 0);
    strokeWeight(1);
    noFill();
    for (int i = 0; i < 180; i++) {
      if (oldValue[i] - newValue[i] > 35 || newValue[i] - oldValue[i] > 35) {
        x = radius + cos(radians((180+i)))*(newValue[i]);
        y = radius + sin(radians((180+i)))*(newValue[i]);
        ellipse(x, y, 10, 10);
      }
    }
  }
/* set the radar distance rings and out put their values, 50, 100, 150 etc.. */
  for (int i = 0; i <=6; i++) {
    noFill();
    strokeWeight(1);
    stroke(0, 255-(30*i), 0);
    ellipse(radius, radius, (100*i), (100*i)); 
    fill(200, 200, 200);
    noStroke();
    text(Integer.toString(radarDist+50), 380, (305-radarDist), 50, 50);
    radarDist+=50;
  }
  radarDist = 0;
/* draw the grid lines on the radar every 30 degrees and write their values 180, 210, 240 etc.. */
  for (int i = 0; i <= 6; i++) {
    strokeWeight(1);
    stroke(0, 55, 0);
    line(radius, radius, radius + cos(radians(180+(30*i)))*w, radius + sin(radians(180+(30*i)))*w);
    fill(200, 200, 200);
    noStroke();
    if (180+(30*i) >= 300) {
      text(Integer.toString(180+(30*i)), (radius+10) + cos(radians(180+(30*i)))*(w+10), (radius+10) + sin(radians(180+(30*i)))*(w+10), 25, 50);
    } else {
      text(Integer.toString(180+(30*i)), radius + cos(radians(180+(30*i)))*w, radius + sin(radians(180+(30*i)))*w, 60, 40);
    }
  }
/* Write information text and values. */
  noStroke();
  fill(0);
  rect(350, 402, 800, 100);
  fill(0, 100, 0);
  text("Degrees: "+Integer.toString(degree), 100, 380, 100, 50);  // use Integet.toString to convert numeric to string as text() only outputs strings
  text("Distance: "+Integer.toString(value), 100, 400, 100, 50);  // text(string, x, y, width, height)
  text("Radar Plot Graph ", 540, 380, 250, 50);
  fill(0, 50, 250);
  textSize(22);
  text("SP ROBOTICS : RADAR ", 570, 480, 650, 200);
  textSize(12);
  fill(0);
  rect(70, 60, 150, 100);
  fill(255, 255, 0); 
  text("Screen Key:", 100, 50, 150, 50);
  fill(0, 50, 250);
  rect(30, 53, 10, 10);
  text("First sweep", 115, 70, 150, 50);
  fill(0, 110, 0);
  rect(30, 73, 10, 10);
  text("Second sweep", 115, 90, 150, 50);
  fill(0, 170, 0);
  rect(30, 93, 10, 10);
  text("Average", 115, 110, 150, 50);
  noFill();
  stroke(150, 0, 0);
  strokeWeight(1);
  ellipse(29, 113, 10, 10); 
  fill(150, 0, 0);
  text("Motion", 115, 130, 150, 50);
}

/* get values from serial port */
void serialEvent (Serial arduinoport) {
  String xString = arduinoport.readStringUntil('\n');  // read the serial port until a new line
  if (xString != null) {  // if theres data in between the new lines
    xString = trim(xString); // get rid of any whitespace just in case
    String getX = xString.substring(1, xString.indexOf("V")); // get the value of the servo position
    String getV = xString.substring(xString.indexOf("V")+1, xString.length()); // get the value of the sensor reading
    degree = Integer.parseInt(getX); // set the values to variables
    value = Integer.parseInt(getV);
    oldValue[degree] = newValue[degree]; // store the values in the arrays.
    newValue[degree] = value;  
 /* sets a counter to allow for the first 2 sweeps of the servo */
    firstRun++;
    if (firstRun > 360) {
      firstRun = 360; // keep the value at 360
    }
  }
}

07/14/2017

Arduinoで(太陽)光の方向を追いかける2軸サーボ(New Algorithm)

Img_20170708_153936 Img_20170708_153958 Img_20170706_101405 Img_20170706_101420













Arduino Nanoで(太陽)光の方向を追いかける装置をブレッドボード上に作ってうまく稼働したので、小さなユニバーサル基板(60mm x 40mm)の上に部品を置いて製作してみた。
ケースも100均ショップで、適当な大きさの円筒形貯金箱を入手したので、その中に基板を入れ、ケース上面にLDRセンサー(前回作ったもの)を装着したサーボを固定した。

装置が新しくなったので、それに併せて光を追いかけるスケッチも自分のオリジナルな発想でロジックを考え、光の方向を探索する時間も短縮できるようにした。その結果、光を追いかける速度は格段に速くなり、室内などでテストした際、室内灯を消灯すると、初期の位置に戻り、点灯すると直ちに灯りの位置を向く。またセンサー部に手を翳すと、手の影を嫌ってより明るい方向を向くように動くので、その仕草がなかなか可愛い。

と書いてから、本当に明るい方向を向いているのだろうか・・・と初期の動きが気になったので、センサーを経度方向約170度分を周して、そこで明るい場所を探索の初期値にするようにスケッチを追加した。
緯度方向は、初期値を決め打ちして、探査をしていないので、そのために初期値の緯度・経度値から位置を確定するまでに少し時間がかかる場合がある。

スケッチは当初のものより少し長くなってしまった。しかし、まあまあ納得がいくようになったのでスケッチを公開することにする。
緯度方向の初期値探査が欲しい場合には、私のスケッチに倣って探せば良いし、経度方向の初期探査が不要なら、(34~55行までと関連のある部分を)外せばいい。それでも大きな問題無く稼働すると思う。

《Sketch》

// The Sunlight Tracker New Algorithm  by Savvy 2017.07.14
//
#include <Servo.h>
Servo servohori;
int svLimitMax = 175;
int svLimitMin =   5;
int servoh     = svLimitMin;   // Initial value of search angle

Servo servoverti; 
int svLimitHigh = 120;
int svLimitLow  =   5;
int servov      =  45;  // Initial value of search angle

// Assign the wireing color to LDRs
int ldrtopl = 2;  // top left LDR Green wiring
int ldrtopr = 1;  // top right LDR Yellow wiring
int ldrbotl = 3;  // bottom left LDR Blue wiring
int ldrbotr = 0;  // bottom right LDR Red wiring

int topl;
int topr;
int botl;
int botr;

int hstep;
int vstep;
int alpha;
int  beta;

 void setup () {
  servohori.attach(10);    // horizontal motor servo  (Blue wiring)
  servoverti.attach(9);    // vertical motor servo  (Green wiring)

// search initial horizontal angle between servo limit min-max angle
  int i    = 0;
  int imin = 0;
  int lx[]  = { 9999, 9999, 9999, 9999, 9999, 9999, 9999};
  int hstp  = (svLimitMax - svLimitMin) / 6;
  int lxmin = 9999;
  alpha = svLimitMin;
  beta  = servov;

  for ( i=0; i<7; i++ )
  {
   lx[i] =  wlight( alpha, beta);
   alpha = alpha + hstp;
   if( lx[i] < lxmin )
   { lxmin = lx[i];
    imin = i;
     }
  }
  delay(200);
  // this is the initial location of horizontal servo
  servohori.write( svLimitMin + hstp*imin );
}
void loop(){
  // Read the servo set angle at the moment
  servoh = servohori.read();
  servov = servoverti.read();
  
  // Read the light number of each LDR
  topl = analogRead(ldrtopl);
  topr = analogRead(ldrtopr);
  botl = analogRead(ldrbotl);
  botr = analogRead(ldrbotr);
  
  // calculate the average value
  int avgtop   = (topl + topr) / 2;   // average of top LDRs
  int avgbot   = (botl + botr) / 2;   // average of bottom LDRs
  int avgleft  = (topl + botl) / 2;   // average of left LDRs
  int avgright = (topr + botr) / 2;   // average of right LDRs

  if( (avgtop < 1012) & (avgbot < 1012) )  // the servo acts while in the light
  {
    hstep = xstep(avgright,avgleft);  // determination of the horizontal serch step angle
    servoh = servoh + hstep;
    
    if((svLimitMin < servoh) & (servoh < svLimitMax))  // acts between max - min limits
    { 
       servohori.write(servoh);
    }
    
    vstep = xstep(avgtop,avgbot);   // determination of the vertical serch step angle 
    servov = servov + vstep;
    
    if((svLimitLow < servov) & (servov < svLimitHigh))
    {
        servoverti.write(servov);
    }       
  }
  delay(200);
}
int xstep(int avg1, int avg2)
  // classify the step value from the two light value
{
  int jcase = avg1 - avg2;
  int result;
    
    if(jcase < -30 ) {
      result = 10; 
    }
    if((-30 <= jcase) & (jcase < -10)){
      result = 3;
    }
    if((-10 <= jcase) & (jcase <  -2)){
      result = 1;
    }
    if(( -2 <= jcase) & (jcase <  2)){
      result = 0;
    }
    if(( 2 <= jcase) & (jcase < 10)){
      result = -1;
    }
    if(( 10 <= jcase) & (jcase < 30)){
      result = -3;
    }
    if( 30 <= jcase ){
      result = -10;
    }
    return result;
}
int wlight(int alpha, int beta)
{
   servohori.write(alpha);
   servoverti.write(beta);
   int loc  = (analogRead(ldrtopl) + analogRead(ldrtopr) + analogRead(ldrbotl) + analogRead(ldrbotr)) / 4; 
   delay(200);
   return loc;
}

07/04/2017

Arduinoで作る太陽光の方向を向く2軸サーボ

Img_20170704_203131 Img_20170629_224615 Arduino Nano (ATmega328)を使って、(太陽)光のある方向を向く2軸サーボ装置を作ってみた。
・Arduinoは、 Nano を
・ServoMotorは、 SG92R を 2個使い
・フォトレジスターは、 手元にあったものを 4個使った。はじめは、抵抗値の基本値を選ぶ必要があるのか・・・と思ったが、ロットが揃っているものなら、それを使って良さそうだ。

Webで見つけたサイト「Arduino Solar Tracker(http://www.electronicshub.org/arduino-solar-tracker/
」などを参考に製作した。

Img_20170704_090341 Img_20170704_090529 Img_20170704_090634 フォトレジスタと抵抗の固定は、小型のユニバーサル基板の上に半田付けし、光の方向を捉え易くするために、写真のように十字型の覆いを設けた。この部分の回路図は、参考にさせてもらったブログの通りであると思う。

基板の後ろから見て、右下(赤色の配線⇒A0)、右上(黄色の配線⇒A1)、左上(緑色の配線⇒A2)、左下(青色の配線⇒A3)とした。

サーボモーターは、水平方向の制御線を水色⇒D10へ、垂直方向の制御線を白⇒D9へと繋いだ。

Solar_trackerflow_chart_20170704 最初、スケッチは参考にしたブログのものをそのまま使ったが、収束に時間がかかる事と、収束した状態でもサーボを動かしているので、左図のようにフローチャートを作成して、収束時間を短縮し、収束したらサーボモータを停めるようにスケッチを変更した。細かな所は、左図を確認すれば解るだろうと思う。


スケッチは、制御の状態を確認したかったので、オリジナルのスケッチに対してシリアルモニターに途中の状態を出力するように追記した。

出力結果を確認し、光源の位置から離れていると思われる場合には、光源に向かうサーボモータ回転角の刻み幅を大きくし、光源に近づいてくると、刻み幅を小さくするようにした。


閾値は明るさの差 eps を”5”としたが、動き方を見て調整するのが良いだろう。

稼働させてみると、光のある方向への収束時間は随分早くなったように思う。

スケッチは、下記のようである。

// The Sunlight Tracker 2017.07.04
//
#include <Servo.h>
Servo servohori;
int servoh = 90;
int servohLimitHigh = 170;
int servohLimitLow = 10;
int hstep = 1;
int eps = 5;

Servo servoverti; 
int servov = 30; 
int servovLimitHigh = 120;
int servovLimitLow = 10;
int vstep = 1;

//Assigning LDRs
int ldrtopl = 2; //top left LDR Green wiring
int ldrtopr = 1; //top right LDR Yellow wiring
int ldrbotl = 3; // bottom left LDR Blue wiring
int ldrbotr = 0; // bottom right LDR Red wiring

 void setup () {
  Serial.begin(9600);   // to check the sketch, print out to the monitor
  servohori.attach(10); // horizontal motor servo  (Blue wiring)
  servohori.write(servoh);
  servoverti.attach(9); // vertical motor servo  (Green wiring)
  servoverti.write(servov);
  delay(500);
 }

void loop(){
  servoh = servohori.read();
  servov = servoverti.read();
  Serial.print("Location of Servo   Horizontal:");
  Serial.print(servoh);
  Serial.print("deg,  Vertical:");
  Serial.print(servov);
  Serial.println("deg");
  
  //capturing analog values of each LDR
  int topl = analogRead(ldrtopl);
  int topr = analogRead(ldrtopr);
  int botl = analogRead(ldrbotl);
  int botr = analogRead(ldrbotr);
  Serial.println(" ");
  Serial.println("Captured LDR value ");
  Serial.print("TL:");
  Serial.print(topl);
  Serial.print("  TR:");
  Serial.println(topr);
  Serial.print("BL:");
  Serial.print(botl);
  Serial.print("  BR:");
  Serial.println(botr);
  
  // calculating average
  int avgtop = (topl + topr) / 2;   //average of top LDRs
  int avgbot = (botl + botr) / 2;   //average of bottom LDRs
  int avgleft = (topl + botl) / 2;  //average of left LDRs
  int avgright = (topr + botr) / 2; //average of right LDRs

  if (avgtop < avgbot) {
    Serial.print("Average Value  ");
    Serial.print("Top:");Serial.print(avgtop);
    Serial.print(" < ");
    Serial.print("Bottom:");Serial.println(avgbot);
    
    vstep = abs(avgtop-avgbot);
    if( vstep < eps ){
      Serial.println("Top is lighter than Bottom =< turn Up");
      vstep = 1 + vstep/4;
      Serial.print("servov = ");Serial.print(servov);Serial.print("+");
      Serial.println(vstep);
      servoverti.write(servov + vstep); 
      }
    else {  // to stabilize the servo-motor
      Serial.print("|Top-Bottom|=<");Serial.print(eps);
      Serial.println(" then Stop the Vertical servo");     
    }
    if (servov > servovLimitHigh) { 
      servov = servovLimitHigh;
     }
    delay(10);
  }
  else if (avgbot < avgtop) {
      Serial.print("Average Value of LDR  ");
      Serial.print("Bottom:");Serial.print(avgbot);
      Serial.print(" < ");
      Serial.print("Top:");Serial.println(avgtop);

    vstep = abs(avgtop-avgbot); 
    if( vstep > eps ){   
      Serial.println("Bottom is lighter than Top => turn Down");
      vstep = 1 + vstep/4;
      Serial.print("servov = ");Serial.print(servov);Serial.print("-");
      Serial.println(vstep);     
      servoverti.write(servov - vstep);
    }
    else {  // to stabilize the servo-motor
      Serial.print("|Top-Bottom|=<");Serial.print(eps);
      Serial.println(" then Stop the Vertical servo");       
    }
    if (servov < servovLimitLow) {
    servov = servovLimitLow;
    }
    delay(10);
  }
  else {
  }
  
  if (avgleft > avgright) {
      Serial.print("Average Value of LDR  ");
      Serial.print("Left:");Serial.print(avgleft);
      Serial.print(" > ");
      Serial.print("Right:");Serial.println(avgright);

      hstep = abs(avgleft-avgright);
    if( hstep > eps ){  
      Serial.println("Right is lighter than Left => turn Right");
      hstep = 1 + hstep/4;
      Serial.print("servoh = ");Serial.print(servoh);Serial.print("+");
      Serial.println(hstep);
      servohori.write(servoh + hstep);
    }
    else {   // to stabilize the servo-motor
      Serial.print("|Left-Right|=<");Serial.print(eps);
      Serial.println(" then Stop the Horizontal servo");         
    }
    if (servoh > servohLimitHigh) {
    servoh = servohLimitHigh;
    }
    delay(10);
  }
  else if (avgright > avgleft) {
      Serial.print("Average Value of LDR  ");
      Serial.print("Right:");Serial.print(avgright);
      Serial.print(" > ");
      Serial.print("Left:");Serial.println(avgleft);    

    hstep = abs(avgleft-avgright);
    if( hstep > eps ){   
      Serial.println("Left is lighter than Right => turn Left");
      hstep = 1 + hstep/4;
      Serial.print("servoh = ");Serial.print(servoh);Serial.print("-");
      Serial.println(hstep);     
      servohori.write(servoh - hstep);
    }
    else {  // to stabilize the servo-motor
      Serial.print("|Left-Right|=<");Serial.print(eps);
      Serial.println(" then Stop the Horizontal servo");
    }
    if (servoh < servohLimitLow) {
      servoh = servohLimitLow;
     }
    delay(10);
  }
  else  {
  }
  delay(50);
}


《備考》
ここに、
 hstep : 水平方向回転サーボモータの回転刻み角度
  vstep : 垂直方向回転サーボモータの回転刻み角度
 esp : 左右、上下のフォトレジスタから読取った値を比較した際のサーボモータの停止判定値

問題は、周囲が暗くなると、灯りを探し回るので、この動きを止める必要がありそうだ。
多分、フォトレジスタで計測した際の明るさが、ある程度大きくなったら、サーボモータの動きを止めれば良いと思う。これは、次にソーラーパネルと組み合わせる計画があるので、その際に改造するつもりだ。

06/11/2017

Processing 3.3.4 のインストール

Arduino で Sonic Radar の画像処理を行うには、Processingが必要な事が分かったので、早速インストールしてみることにした。
他のアプリと同じようにやってみてもインストールされないし・・・

ネットで調べてみると、「Processing2のダウンロードとインストール」が見つかり、参考にしてWindows64bit版で試してみた。

ところが、解凍しても上手くいかない・・・僕が使った解凍ソフトは、お気に入りの
Lhaplus V1.73 (2017/06/11時点の最新版は 1.74)」だが、エラーが発生し、巧くゆかないようだ。それで「7-Zip」で解凍してみた。問題無く解凍出来る。

Cドライブの Program Files の中に、僕は「Processing」というフォルダを作成し、解凍したファイルを全て移動させた。
参考にさせて戴いたブログのProcessing v2 には「launch4j」というフォルダがあるが、僕が解凍したものでは、そのフォルダは存在していなかったので、解凍内容が少し違っている。

そして、「Processing.exe」のショートカットアイコンをデスクトップに貼り付けた。

Provessing3 デスクトップの『Processing ショートカットアイコン』をクリックすると、左図のように起動した!


05/27/2017

肩を温める USB電源(5V)を使った自作ヒーター

Img_20170527_071241 50肩になってしまった。就寝中に肩が冷えると痛みが増す。肩部を温めるための何かいい方法は無いものかと探していたが、そのようなものは、意外に見つからない。別の電子工作のアイデアを探していた時、カメラレンズや鏡筒のための「結露対策ヒーターの作り方」というブログに出会った。
USB(5V)から電源をとり、1mほどの長さのニクロム線に500mAの電流を流してレンズなどを温めるというアイデアになっている。この方法を応用できないか・・・とやってみた。


僕は、Amazonから これに必要な部品①,②を購入
①単位長さ当たりの抵抗値が、参考にしたブログに記載のものと同等レベルのニクロム線 (557円、送料込み)
  ワイヤ径0.4mm、  抵抗値 8.674Ω/m、 AWG26、 15m巻き
  使うのは1mほどの長さなのだが、この単位でしか購入できなかった。

注記)抵抗線は、線径が細くなるほど単位長さ当たりの抵抗値が高くなる。
 用途に応じて線径を決めるのが良い。
  (例) 矢印(⇒)の右側は、10Ωになる抵抗線(ニクロム線)の概略長さ
    0.7mm - 2.93Ω/m ⇒ 341cm
    0.6mm - 4.103Ω/m ⇒ 244cm
    0.5mm - 5.755Ω/m ⇒ 173cm 
    0.4mm - 8.674Ω/m ⇒ 115cm
    0.3mm - 15.42Ω/m ⇒ 65cm
    0.25mm - 22.21Ω/m ⇒ 45cm

②耐熱ガラスチューブ内径1.5mm (@156円+配送料560円)
  1mの長さがこの値段。使うのは1.2mほどになるので、1mでは短い・・・
  不足する長さの分については、僕は細目の熱収縮チューブを使った。
  実は、細い熱収縮チューブも購入し、ニクロム線をその中に入れようとしたが、50cmほど挿入したところから、挿入する抵抗が高くなり、1mのニクロム線を貫通させることは無理そうなので、耐熱ガラスチューブを使うことにした。この問題は透水性がありそうなので、電気の絶縁性能がどうかな・・・と不安である。

 その他、アッセンブリに必要な部品として、熱収縮チューブ、圧着スリーブ、自己融着テープ、USBケーブル、モバイルバッテリー等があるが、身の回りにあるものから、かき集めた。

参考にしたブログにも注意事項が細かく書かれていたが、
私がここに書いているのは私のメモであって、これと同じようなものを作ろうとして、いかなる事故が起きたとしても、私は一切の責任を負いません。自作するに際しての責任は、全てあなたの自己責任になります。

《作り方》

Img_20170526_115611 Img_20170526_120530結露対策ヒーターの作り方」を参考に、5V、500mAでほのかに温かいという仕様にすべく、抵抗線(ニクロム線)の値も市販品から 8.674Ω/m のものを選んで、入手後プラスチックスケールの上に抵抗線を貼り付け、20cm当たりの抵抗値を実測してみた。
抵抗値は 1.7Ωであった。

そのため、抵抗値が10Ωになるように、抵抗線の長さをほぼ 118cmと決めた。

Img_20170526_125638 Img_20170526_132144 Img_20170526_124651 Img_20170526_132550 ニクロム線は、絶縁のない裸状態なので、ショートさせたりする危険性を避けるために、
耐熱ガラスチューブを被せ、ガラスチューブ長さが不足する両端部を細目の熱収縮チューブを被せて、写真のようにニクロム線を被覆した。
バッテリーに繋いでみたが、この状態ではずっと手で握って居られるほど低い温度である。
多分、
耐熱ガラスチューブの効果が高すぎるのかもしれない。しかし、ガラスチューブ表面の温度は時間が経過すれば、内部も外部も同じ状態(平衡状態)になるはずである。

耐熱ガラスチューブを使わず、熱収縮チューブを使って絶縁するという(参考にしたブログと同じ)方法もあるが、細い熱収縮チューブを使うと、0.4mmの径のニクロム線を1mの長さ通すのは至難の業が必要になる。ガラスチューブは接触抵抗が低いので、難なく通すことが出来る。

肩用のサポータにこのニクロム線を縫い付けるのだが、ニクロム線の長さはある程度必要なため、温度を上げるためにニクロム線長さを短縮したくはないので、当初計画通り10Ωを保持する長さのままとした。

Img_20170526_133605 Img_20170526_161449 Img_20170526_161513 USBケーブルの端を開き、圧着スリーブを用いてニクロム線と接続した。この上にも太めの熱収縮チューブを被せ、その後、自己融着テープを用いて絶縁保護とその部分の補強を行った。


《使用の状況》

温かさ : 組込む前に、もう少し細かく詰めれば良かった。温度は低めで、もう少し上げられれば・・・と思う。

ニクロム線の配置間隔をもう少し詰めた方が良さそうに思う。そのためには、ニクロム線の単位長さ当たりの抵抗値をもう少し低いものを使って、扱える線の長さを延ばし配置すればと考えられる。

例えば前述の「線径-単位長さ当たりの抵抗値」を参考に、0.6mmの線径の抵抗線を使えば、10Ωとするには244cmとれる為、今回使った線径0.4mmの線配置の空いた場所に線を配置できる。そのため、面当たりに近づき、より「ほんわか」した温かさを実現できそうだ。

より以前の記事一覧

December 2017
Sun Mon Tue Wed Thu Fri Sat
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31            

Recent Trackbacks

無料ブログはココログ