2020年12月31日木曜日

WS2812BマトリクスLEDで電光掲示板

冬らしく寒くなった大晦日の今日、午前中に木工工作の締めの作業でニス塗を行いましたが、気温が低くニスがあまり伸びず少しムラができてしまいました。仕様では5℃以下では塗布しないようにとのことだったので、作業部屋の室温をみたら9℃だったので敢行したのですが。まあ、仕方ないですね。

午後は電光掲示板のコードの仕上げと動作撮影・編集です。追加注文してあるWS2812Bパネルはまだ届きません。先日、国内に届いたと書いたのは間違いで、届いているのはつい最近注文した別のものでした。あとから注文したものがこんなに早いと思わなかったので勘違いしてました。

という訳でパネル2枚で撮影した電光掲示板の動画です。iPhoneで撮影しましたが、LEDの輝度が高く自動で絞りが効いてしまいました。スミアも出でしまっています。肉眼だともっと明るくハッキリくっきり見えます。


動画の最後のネギ振りは、ソース内で制御しています(下記スケッチ参照)。

--追記--
2年強ぶりにパネル3枚を追加購入して、計5枚で試してみました。

回路図

バグがあるかも知れません、無保証です。

使い方

microSD内にconfig.txtファイル、表示するテキスト(ShiftJIS)を収めたmessage.txt、bmpファイル(高さ16pixel以下)を格納します。


config.txt でスクロール速度や輝度、デフォルトカラーを指定しています。
表示するメッセージは、message.txt内に記述します。

テキスト内に以下のような制御コマンドを記載できます。
<!red!>赤い文字<!white!>白い文字<!/hoge.bmp!>


スケッチ

configやテキストファイルのパーサは簡易的なものです。記述エラーがあるとおかしなことが起こるかもしれません。バグがあるかも知れません、無保証です。著作権は留保しますが、改変などご自由にどうぞ。

以下のライブラリを使っています。感謝です。
・WS2812B LEDドライバ

・東雲フォントライブラリ

・bmpファイルデコーダ

//---------------------------------------------------------------------
//                                                     2020.12.31 naka
//  16x16ドットマトリックスLED(WS2812B)電光掲示板 
//  
//  ・microSDに格納したテキストファイル(ShiftJIS)、bmp画像を表示
//  ・config.txtファイルにスクロール速度、テキストの表示色デフォルト、輝度定義
//  ・表示するテキストに文字色、bmpファイル名を埋め込み可能
//    例)<!red!>赤い文字<!white!>白い文字<!/hoge.bmp!>
//---------------------------------------------------------------------
#include <ESP32_SPIFFS_ShinonomeFNT.h>
#include <SD.h>
#include <FS.h>
#include <Adafruit_NeoPixel.h>
#include "BITMAPDecoder.h"

// IOポート
#define PIN       5 // Neopixel 制御用ピン番号
#define SD_CS    16 // SD card chip select

// configデフォルト
uint8_t text_r = 255;
uint8_t text_g = 255;
uint8_t text_b = 255;
int     scroll_wait_ms = 40;
int     brightness = 5; // 1~100
int     brightness_div = 100 / brightness;

// マトリクスパネルサイズ
#define UNIT_W      16                // 1枚のパネルのピクセル数(横)
#define UNIT_H      16                // 1枚のパネルのピクセル数(縦)
#define UNIT_NUM    2                 // 並べるパネルの枚数
#define NUMPIXELS  (UNIT_W*UNIT_H*UNIT_NUM) // LED数

Adafruit_NeoPixel         pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
ESP32_SPIFFS_ShinonomeFNT SFR;
BITMAPDecoder             bitmap = BITMAPDecoder();
uint32_t           scrollBuff[UNIT_H]; // パネル1列分

// LEDパネルのスクロール(デフォルトは1列スクロール)
void scrollPixel(int16_t n = 1) {
  int16_t i,x,y;
  for (i=0;i<n;i++) {
    for (x=UNIT_W*UNIT_NUM-1; x>=0; x--) {
      for (y=0;y<UNIT_H;y++) {
        setPixel(x+1,y,getPixel(x,y));
      }
    }
    for (y=0;y<UNIT_H;y++) {
      setPixel(0,y,scrollBuff[y]);
      scrollBuff[y] = 0; // クリア
    }
    scroll_wait(scroll_wait_ms);
    showPixel();
  }
}

// bmpファイルのスクロール表示
void scrollImgFile(char* fileName) {
  File bmpFile = SD.open(fileName, "r");
  bitmap.checkFile(bmpFile);

  for (uint8_t x=0; x<bitmap.width(); x++) {
    for (uint8_t y=0; y<16; y++) {
      uint8_t r,g,b;
      PIXEL p = bitmap.readPixel(bmpFile,x,y);
      r = p.r/brightness_div; g = p.g/brightness_div; b = p.b/brightness_div;
      scrollBuff[y] = pixels.Color(r,g,b);
    }
    scrollPixel();
  }
  bmpFile.close();
}

// bmpファイルの表示(右上角がLEDパネルの右上;offsetxで左にズレる)
void dispImgFile(char* fileName, uint16_t offsetx=0) {
  File bmpFile = SD.open(fileName, "r");
  bitmap.checkFile(bmpFile);
  
  for (uint8_t x=0; x<bitmap.width(); x++) {
    for (uint8_t y=0; y<16; y++) {
      uint8_t r,g,b;
      PIXEL p = bitmap.readPixel(bmpFile,x,y);
      r = p.r/brightness_div; g = p.g/brightness_div; b = p.b/brightness_div;  
      setPixel(offsetx+bitmap.width()-x-1,y,pixels.Color(r,g,b));
    }
  }
  showPixel();
  bmpFile.close();
}

// LEDパネルクリア
void clearPixel() {
  uint16_t pos;
  for (pos=0; pos<UNIT_H; pos++)
    scrollBuff[pos] = 0;
    
  for (pos=0;pos<NUMPIXELS;pos++)
    pixels.setPixelColor(pos, 0);
  pixels.show();
}

// LEDパネル状のx,y位置(x=0,y=0がパネルの右上)をLEDの番号に変換
uint16_t xy2pos(uint16_t x,uint16_t y) {
  uint16_t pos; // シリーズに連結されたLEDの番号(右上0起点)
  if (y%2==0) { // 偶数行
     pos = UNIT_W * y + x;
     pos =  pos + UNIT_W * UNIT_H * (int)(x/UNIT_W) - UNIT_W * (int)(x/UNIT_W);
  }
  else {         // 奇数行
     pos = UNIT_W * y + (UNIT_W - x) - 1;
     pos = pos + UNIT_W * UNIT_H * (int)(x/UNIT_W) + UNIT_W * (int)(x/UNIT_W);;
  }
  return pos;
}

// x,y座標にカラーrgb値を設定
void setPixel(int16_t x, int16_t y, uint32_t rgb) {
  int16_t pos = xy2pos(x,y);
  if (pos<NUMPIXELS)
     pixels.setPixelColor(pos,rgb);
}

// x,y座標のカラーrgb値を取り出す
uint32_t getPixel(int16_t x, int16_t y) {
  int16_t pos = xy2pos(x,y);
  if (pos<NUMPIXELS)
    return pixels.getPixelColor(pos);
  else
    return 0;
}

// LEDパネルの表示更新
uint32_t showPixel() {
  pixels.show();
}

// 1列スクロールするときの待ち時間
void scroll_wait(int msec) {
  static uint32_t prevtime = millis();
  while((millis()-prevtime) < msec);
  prevtime = millis();
}

// 文字フォントのスクロール表示
void scrollFont(uint8_t *fnt, uint8_t R, uint8_t G, uint8_t B) {
  for (uint8_t i=0; i<8; i++) {
    // フォントパターン1列分をスクロールバッファにセット
    for (uint8_t y=0; y<16; y++) {
      if (fnt[y] & (0x80 >> i)) {
        scrollBuff[y] = pixels.Color(R/brightness_div,G/brightness_div,B/brightness_div);
      } else {
        scrollBuff[y] = 0;
      }
    }
    scrollPixel();
  }
}

#define MAX_STRLEN 2
#define FONT_SIZE  16
// 文字のスクロール表示(1文字)
void scrollText(char* msg, uint8_t R, uint8_t G, uint8_t B) {
  uint8_t  fontBuff[MAX_STRLEN][FONT_SIZE] = {0};
  uint16_t fontNum;
  // ShiftJIS文字(1文字)をビットマップに変換(オリジナルのライブラリになかったので追加)
  fontNum = SFR.SjisStrDirect_ShinoFNT_readALL(msg, fontBuff);
  for (int i=0;i<fontNum;i++) {
    scrollFont(fontBuff[i], R, G, B);         
  }
}

#define CNTLBUFF_SIZE 30
#define SPLIT_SIZE 3
// ','区切りでテキストを分割
int splitChar(char* data, char splitdata[][CNTLBUFF_SIZE+1]) {
  int n = strlen(data);
  int splitno = 0;
  int j = 0;
  int i;
  for (i=0;i<n;i++) {
    if (data[i]==',') {
      splitdata[splitno++][j] = '\0';
      j = 0;
      if (splitno>=SPLIT_SIZE)
        splitno = 0;
    }
    else
      splitdata[splitno][j++] = data[i];
  }
  splitdata[splitno++][j] = '\0';
  return splitno;
}

#define COLOR_NUM 8
// テキスト中に埋め込まれた制御コマンドを実行
// 制御コマンド(<! !>の間にある文字列)は以下
//   <!r,g,b!>      ... 文字色R,G,B値(0-255)をカンマ区切り
//   <!color!>      ... 文字色 color:white,red,green,blue,magenta,cyan,yellow,orange
//   <!/hoge.bmp!>  ... 表示ビットマップファイル名
void applyCntl(char* buff) {
  static struct {
          char*   name;
          uint8_t r,g,b;
  } color[COLOR_NUM] =
    {"white"  ,255,255,255,
     "red"    ,255,  0,  0,
     "green"  ,  0,255,  0,
     "blue"   ,  0,  0,255,
     "magenta",255,  0,255,
     "cyan"   ,  0,255,255,
     "yellow" ,255,255,  0,
     "orange" ,255,50, 0};
  
  char splitdata[3][CNTLBUFF_SIZE+1];
  int i;
  if (strlen(buff)==0)
    return;
  else {
    int n = splitChar(buff,splitdata);
    if (n==3) { // r,g,b
      String val = splitdata[0];
      text_r = val.toInt();
      val = splitdata[1];
      text_g = val.toInt();
      val = splitdata[2];
      text_b = val.toInt();
    }
    else if (n==1) {
      int colorFlag = 0;
      for (i=0;i<COLOR_NUM;i++) {
        if (strcmp(splitdata[0],color[i].name)==0) {
          text_r = color[i].r; text_g = color[i].g ; text_b = color[i].b;
          colorFlag = 1;
          break;
        }
      }
      if (colorFlag==0) {
        int m = strlen(splitdata[0]);
        strcmp(splitdata[0],"bmp");
        if (m>5) { // "/x.bmp"
          if (strcmp(&splitdata[0][m-4],".bmp")==0) {
            scrollImgFile(splitdata[0]);       
          }
        }
      }
    }
  }
}

// ShiftJIS文字が全角か判定する
int is_zenkaku(char *sjis){
    unsigned char c = sjis[0];
    if ( (c>=0x81 && c<=0x9f) || (c>=0xe0 && c<=0xfc)){
        c=sjis[1];
        if (c>=0x40 && c<=0xfc) return 1;
    }
    return 0;
}

// テキストファイル内のテイストのスクロール表示(制御コマンドの抽出を含む)
void scrollMsgFile(char* fileName) {
    char buff[3],wk;
    int cntlflag;
    char cntlbuff[CNTLBUFF_SIZE+1];
    char c;
    int zh;
    File textFile;
    textFile = SD.open(fileName,FILE_READ);
    int i = 0, j = 0;
    while(textFile.available()){
      c = char(textFile.read());
      if (c=='\n') {    // 改行コードなら表示中のpixelをすべてスクロール
        scrollPixel(UNIT_W*UNIT_NUM);
        continue;
      }
      buff[i++] = c;
      if (i==2) {
        buff[i] = 0;
        zh = is_zenkaku(buff);
        if (zh==0) { // 半角
          if (buff[0]=='<' && buff[1]=='!') { // 制御コード開始
            cntlflag = 1;
            i = 0;
            continue;
          }
          else if (buff[0]=='!' && buff[1]=='>') { // 制御コード終了
            cntlflag = 0;
            cntlbuff[j] = '\0';
            j = 0;
            applyCntl(cntlbuff);
            i = 0;
            continue;
          }
          else if (cntlflag==1) { // 制御コードバッファリング
             cntlbuff[j++] = buff[0];
             if (j>CNTLBUFF_SIZE) j=0;
             buff[0] = buff[1];
             buff[1] = '\0';
             i = 1;
             continue;
          }
          wk = buff[1];
          buff[1] = 0;
        }
        scrollText(buff,text_r,text_g,text_b);
        if (zh==0) {
          buff[0] = wk;
          i = 1;
        }
        else {
          i = 0;
        }
      }

    }
    if (i>0) {
      buff[i]= 0;
      scrollText(buff,text_r,text_g,text_b);
    }
  textFile.close();
}

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

// config.txtのパース
void parse_config(String line) {
  String key,val;
  int n;
  if (line.length()==0)
    return;
  else if ((n=line.indexOf(':'))>1) {
    key = line.substring(0,n);
    key.trim();
    val = line.substring(n+1);
    val.trim();
    if (key=="text_r")
      text_r = val.toInt();
    else if (key=="text_g")
      text_g = val.toInt();
    else if (key=="text_b")
      text_b = val.toInt();
    else if (key=="scroll_wait")
      scroll_wait_ms = val.toInt();
    else if (key=="brightness") {
      brightness = val.toInt();
      if (brightness>100) brightness = 100;
      else if (brightness<=0) brightness = 1;
      brightness_div = 100 / brightness;
    }
  }
}

// config.txtをロードする
void load_config() {
  char lineData[32];
  int n;
  String line,line2;
  String comment = "#";
  File ConfigFile = SD.open("/config.txt",FILE_READ);
  if (ConfigFile) {
    while ((n = readLine(ConfigFile,lineData))>=0) {
      line  = lineData;
      line.trim();
      if (line.startsWith(comment))
        continue;
      else
        parse_config(line);
    }
    ConfigFile.close();
  }
}

void scrollMsg(char* msg) { // 半角文字のみ
    char buff[2];
    int n = strlen(msg);
    for (int i=0;i<n;i++) {
      if (msg[i]=='\n') {    // 改行コードなら表示中のpixelをすべてスクロール
        scrollPixel(UNIT_W*UNIT_NUM);
        continue;
      }
      else {
        buff[0] = msg[i];
        buff[1] = '\0';
        scrollText(buff,text_r,text_g,text_b);
      }
    }
}

void setup() {
  const char* Shino_Zen_Font_file   = "/shnmk16.bdf";    //全角フォントファイル名を定義
  const char* Shino_Half_Font_file  = "/shnm8x16r.bdf";  //半角フォントファイル名を定義
  SFR.SPIFFS_Shinonome_Init2F(Shino_Half_Font_file, Shino_Zen_Font_file);

  if (!SD.begin(SD_CS, SPI, 24000000)) {
    scrollMsg("microSD Error\n");
  }
  else {
    scrollMsg("OK\n");
    load_config();
    delay(1000);
  }
}
    
void loop() {  
  scrollMsgFile("/message.txt");

  for (int i=0;i<5;i++) {
    dispImgFile("/negi1.bmp",16);
    delay(200);
    dispImgFile("/negi2.bmp",16);
    delay(200);
  }
  scrollPixel(16);
  
  delay(1000);
 }

ESP32_SPIFFS_ShinonomeFNT

ESP32_SPOFFS_ShinonomeFNTライブラリに、ShiftJIS文字列をフォントパターンに変換するSjisStrDirect_ShinoFNT_readALL関数追加しました。オリジナルはUTF8用です。

ヘッダファイルとcppファイルの追加箇所を反転表示しています。



オリジナルにあったUTF8からSJISへの変換をスキップしただけです。
//*******************東雲フォント全変換(SJIS)*************************
uint16_t ESP32_SPIFFS_ShinonomeFNT::SjisStrDirect_ShinoFNT_readALL(String str, uint8_t font_buf[][16]){
  uint8_t sj_txt[str.length()+1];
  int16_t i;
  for(i=0;i<str.length();i++) sj_txt[i] = str[i];
  sj_txt[i] = 0;
  uint16_t sj_length = i;

  ESP32_SPIFFS_ShinonomeFNT::SjisToShinonome16FontRead_ALL(_SinoZ, _SinoH, 0, 0, sj_txt, sj_length, font_buf);
  return sj_length;
}

BITMAP decoder

BITMAP decoderのヘッダとcppソースはスケッチを同じフォルダに置きました。


関連記事

 

2020年12月28日月曜日

年賀状印刷でプリンタが発熱

先週末のクリスマスで仕事納めでした。週末は大掃除と年賀状書きです。毎年年賀状では1年を振り替えって近況報告していますが、今年は旅行やイベントで出かける事がなかったので写真も少なく、ネタに苦戦しました。

なんとか作成が終わり印刷を始めたら、プリンタが頻繁に止まります。1枚印刷するたびに冷却中になります。こんなメッセージ初めてです。寒い部屋なのに発熱?

30秒くらいで再開するのですが、1枚毎に止まるので時間がかかります。去年はこんな事なかったのに、故障の前兆でしょうか? このプリンタは昨年12月に購入したので使い始めて1年ちょっとです。たまにしか印刷しないので稼働時間も少ないのに。

それでもなんとか印刷を終えてポストに投函しました。元日配達は厳しいかな。

配達と言えば、Aliexpress で購入したWS2812Bを使ったマトリクスLEDがまだ届きません。日本に到着しているはずですが、年末で通関が滞っているのかな。あるいは郵便局が年賀状に追われて配達が遅くなっているのか。まぁ急ぐモノではないのでよいですが。

マトリクスLEDを電光掲示板にするプログラムを書いています。microSDに入れたテキストと画像表示が一応できるようになりました。需要があるかわかりませが近々公開します。

2020年12月22日火曜日

天体ショー(木星土星大接近)

昨日12/21が一番接近したようですが見損なったので、本日17:30頃に見ました。

近視のため眼鏡だけではひとつの点しか見えなかったので、小さな単眼鏡を使いました。写真は単眼鏡の接眼部にiPhoneのカメラをくっつけて撮りました。

ちょっと手ブレしていますが、土星の輪らしきモノが見えます。知っているからそう見えるのかも。

2020年12月21日月曜日

WS2812BマトリクスLEDにbmpファイルを表示

WS2812BマトリクスLEDにbmpファイルの画像を表示しようと、 XIAOと1.8インチ小型液晶で小型フォトフレーム のときに使った Adafruit_ImageReader ライブラリを調べたところ、Loadした画像データの各ピクセルにアクセスする方法がなかった。これは液晶ディスプレイに表示するライブラリとセットで使うことが前提のようです。

ググってみても事前にbmpファイルを変換してテキストにする方法しか見つからず、自分でbmpを読むしかないのかと、半分あきらめていたら以下の記事を見つけました。

Arduino IDEのBITMAP decoder

これこそ欲しかったものです。感謝です。

早速組み込んで表示してみました。サンプルはSPIFFSのbmpファイルを読むようになっていましたが、microSDからも読み込めます。


LEDに送り込むRGB値を元画像のRGB値の1/32にして輝度はかなり絞っています。それでもまぶしい。
表示・スクロールしている16ドットのドット絵は、以下のサイトのものを使わせていただきました。


Arduinoに敬意を表しイタリアの国旗を先頭に、日本、UK、USA、サンタ、門松の順に表示しています。UKだけ余白を白にしてみました。門松の絵はよく分かりませんね。

以下のようなbmpファイルをそのままmicroSDに入れて読んで表示しています。


横幅は16ピクセルでなくて、もっと長くても大丈夫(だと思います)。

MicroSDに入れたテキストをマトリクスLEDに表示 で書いた東雲フォントの表示もUTF8ではなく、SJISのままで表示できるようになりました。これでWindowsのメモ帳などで書いたテキストをそのままmicroSDに入れて、表示させることができます。

こちらはにSJISでアクセスするよい方法が見つからず、ESP32_SPIFFS_ShinonomeFNTライブラリにSJIS用メソッドを追加しました。以下が、ESP32_SPIFFS_ShinonomeFNT の作者による解説記事です。こちらにも大いに感謝です。


Aliexpressからメールが届き、追加で注文したマトリクスLEDが国内に届いたらしい。しかし、ここから結構日数がかかることがあるので、どうなるか?! 年内に5枚並べて確認できるか微妙なところです。

2020年12月18日金曜日

深夜にiPhoneのバッテリ残量が突然ゼロに!

以前かみさんが使っていたiPhone 6SをテレワークのBGM用に使っています。ACアダプタに繋ぎっぱなしにしてamazon musicやradikoを流していますが、ここ数日朝になるとバッテリ残量がゼロになる症状が出ています。

以下、バッテリ状態のスクリーンショットですが、午前3時頃に100%から突然0%になっています。昨日は午前0時頃でした。いずれもACアダプタは繋いだままなのに。


何なんでしょう? ちなみにOSは最新の14.3です。バッテリの状態を見ると84%なので、ヘタってはいるけどまだまだ大丈夫だと思う。そもそもACアダプタ繋いであるのにゼロはおかしい。

さらに朝、ACアダプタを繋ぎ直しただけでは起動せず、mac bookに繋ぐと起動します。ホントにおかしな症状です。

2020年12月17日木曜日

WS2812BマトリクスLEDを連結して日本語スクロール

WS2812Bを使ったマトリクスLED 2枚繋いでテキストをスクロール表示できるようになりました。WS2812Bが512個繋がっているところに一番端からシリアルでデータを送り込んで表示していますが、スクロールスピードに十分間に合います。一応、繋ぐパネルの枚数が増えても動くようにコードを書いたつもりです。注文してあるパネルが到着したら試してみます。


前回、テキストをある程度バッファに読み込んだあとで表示したので、これを一文字づつ読んで表示させようとしました。が、ちょっとうまく行きません。読み込んだ文字を東雲フォントを使ってドットに変換するのですが、その変換にある程度時間がかかり、スクロールがギクシャクします。まだ調べていませんが、東雲フォントはESP32のSPIFFSに格納してあり、変換の都度それを読みに行くので時間がかかっているのではないかと思います。MicroSDに置いておいた方がアクセスが早いかも。

という訳で、今は100文字単位にバッファに溜めて、一気にドットに変換してから表示しています。101文字目を表示するときに一瞬スクロールが止まります。10文字単位にしたら早いかと思って試しましたがあまり変わらない感じでした。

スケッチはもう少し色々試して完成度を上げてから公開するつもりです。年内になんとかなるかな。

2020年12月15日火曜日

MicroSDに入れたテキストをマトリクスLEDに表示

ESP32にmicroSDを繋いでマトリクスLEDにテキストを表示させてみました。これまではスケッチに直接文字を埋め込んでいましたが、これでコードを書き換えずに表示を変えることができます。configファイルのようなものも入れておいて、色やスクロール速度の設定、16x16のドット絵なども入れられそう。


コードに埋め込んでいた時はUTF8から東雲フォントのSJISへの変換が必要でしたが、microSDから読むならSJISのままでよいはずです。まだ、試していません。Windowsパソコンでテキストを入力するならSJISのままの方が便利。

現時点では一旦バッファに読み込んだテキストを表示していて長いテキストを表示できません。バッファサイズを大きくしてみましたが、どこかでメモリ破壊が起こるようで再起動してしまいます。microSDから読みながら表示できるようにして、長さ制限をなくしたい。

microSDソケットは秋月のDIP変換基板を使っています。


次はパネルを連結して、横32pixelでの表示にトライしてみたいと思っています。

2020年12月14日月曜日

指紋が無くなってしまい超不便

土日に木工作業で紙ヤスリを使ったら、指紋が無くなってしまいました。広いところは電動サンダを使ったのですが、細かい箇所が結構あって手袋もできず素手で作業したのが原因。乾燥も相まって指先がツルツルです。

一番困ったのが、デジタル機器の指紋認証でした。最初iPhoneが開かず壊れたかと疑いましたが、壊れていたのは自分の指先でした。両手の親指と人差し指が使えなくなって、登録した指が全てアウトでした。

マスクしたら顔認証が使えなくなって不便なのと同じでしょうか? なお未だに iPhone 6S なので顔認証は使えません。指紋認証ができなくなって一番不便な機器です。

仕事で使っているmac bookの指紋センサです。これもロック画面の解除で頻繁に使うので、使えないと不便。

ネットを見たりプログラムを書くのに使っているThinkPadも指紋認証なのです。パスワードを忘れかけていた。

iPhone 6SはOSをバージョンアップしてないので使えないアプリがあり、iPadも使っています。これも不便です。

指紋認証が当たり前になっていて普段は意識してないのですが、使えなくなって改めてその有り難さを感じています。
さて指紋はどの位で復活するのでしょうか。

2020年12月12日土曜日

マトリクスLEDに東雲フォントを表示

WS2812Bを使った16x16マトリクスLEDシートに、フリーの東雲(しののめ)フォント16x16を表示させることができました。クリスマスツリー表示の途中で電光掲示板のように文字がスクロール表示されます。



ネットで先人の皆様の記事を色々と読ませていただき、参考になりました。感謝です。


ESP32でのタイミング問題

当初、WS2812BへのRGBデータの送出はライブラリを使わずに自分でコードを書いてました。一度、NeoPixelテープ用のライブラリを使ったのですが、16x16=256個のLEDは表示できず(100個くらいまでしか表示できなかった)、以下を参考にさせていただいて書いてました。


しかし、ESP32でやると光らせるつもりのないLEDが点灯するのです。0/1のパルスの幅を色々と調整したら、発症が少なくなりましたが、それでも時々光ります。ググりまくったら、Githubで以下の議論が行われていました。


ESP32は、1ms毎に割り込みが入り、5μs程度の空白が発生するようです。これにより、WS2812Bへの0/1信号パルス(1.25us)のタイミングがズレる。16x16=256個のLEDにRGBデータ3バイトを送ると8ms近くかかるので途中で必ず割り込みが入ります。

また、WiFiを使うと全くダメというようなことも書いてあり、WiFiで制御しようとしていた目論見は外れました。確かに他の処理を行っていたら、1us程度のシビアなタイミング調整は無理ですね。表示用と通信用で2個のESP32を使えばなんとかなるか? でもその2個間の通信によるタイミングのずれも起こるからやっぱりダメか。

上記Githubスレッドで、ESP32で大丈夫な関数を書いたという人がいて(今年の10月なのでまだホヤホヤ)、最新のライブラリに入っていることが分かりました。感謝。そこで以下の最新ライブラリをインストールして、表示部分のスケッチを書き直したら、指定外のLEDが光ることもなくなり無事に動作したようです。

・Adafruit_NeoPixel ライブラリ(ArduinoIDEのライブラリ管理からインストール可)


東雲フォント

16x16東雲フォントは1MB以上あるのでESP32のSPIFFS領域(プログラムなどを書き込むフラッシュメモリをストレージに使う)にアップロードしました。SDカードでも使えるようですが、SPIFFSならESP32だけで済むので。

SPIFFS領域のパーティションを変更して、2GB使えるようにします。以前はcsvファイルを直接いじっていたようですが、今はArduinoIDEからできました。


ファイルのアップロードは、ESP32 Sketch Data Upload という アドインtool を組み込んで行いました。スケッチと同じフォルダにdataフォルダを作成し、そのなかにフォントファイルを入れておきこのツールを起動するとアップロードされます。このときシリアルモニタを開いておくとエラーになるので、閉じておく。

以下の3つのファイルをdataフォルダに格納しておきます。
/shnmk16.bdf
/shnm8x16r.bdf
/Utf8Sjis.tbl


フォントファイルとUTF8/SJIS変換テーブルをSPIFFSにアップロードした様子です。この表示を行うスケッチがSPIFFSのスケッチ例にあります。



東雲フォントのアクセスは、以下のライブラリを使わせてもらいました(Githubにあります)。

ESP32_SPIFFS_ShinonomeFNT ... 東雲フォントにアクセスする
ESP32_SPIFFS_UTF8toSJIS ... ArduinoIDEのUTF8を東雲フォントのSJISに変換する

スクロール表示

以下の美咲フォント(8x8)をスクロール表示するスケッチを参考にさせて頂き、というかほぼ流用させて頂きました。とても分かりやすいスケッチです。



ひとまず以上です。

このあとは2枚のマトリクスLEDシートをシリーズに繋いでみようと思っています。さらに追加で3枚のシートを注文したので、届けば5枚繋げられます。5枚で1,280個のLEDになり、1280×3×8bit=30,720bitです。エラー無しにちゃんとデータが繋がっていくのか!?

2020年12月10日木曜日

ESP32でNeoPixelマトリクスLED表示

前回、Arduino UNOでマトリクスLEDへ表示したクリスマスツリー をESP32に載せてみました。信号のタイミング調整に少し苦労しました。一応動いていますが、週末にオシロで確認してみようと思います。ESP32は高級すぎて動きがよくわかりません(^_^;


ESP32は信号レベルが3.3Vなので、5Vへのレベル変換回路をかませています。秋月で売られているBSS138を使ったものと同じ回路を組んでみました。秋月のものは4信号が一つのモジュールになっていますが、今回は1本でよいので1回路分だけです。

ESP32に載せたのは、WiFi経由でコントロールしてみたいのと、16x16ドットの東雲フォントを表示したいためです。さて、上手く行くでしょうか?


2020年12月6日日曜日

フルカラーマトリクスLEDでクリスマスツリー

一昨日、中国からフルカラーマトリクスLEDパネル(シート?)が届きました。3週間位前に注文していたもの。マイコン内蔵でシリアル接続できるフルカラーLED WS-2812B(ホンモノか不明)が256個繋がっています。Aliexpressで1,250円位(送料込み)でした。

フレキ基板に載っているので、フニャフニャと曲がります。真っ平らにするのが難しい。

裏面側にハーネスが付いています。3本のうち赤が電源(5V)、白がGND、緑が信号です。このパネルをさらにシリーズに接続できるように入力と出力があります。真ん中の2本は別途電源を供給するもの(だと思う)。

動作確認のためArduino UNOでクリスマスツリーを光らせるプログラムを書いてみました。色の違うドットは乱数で色が変わり、チカチカします。

明るく点灯させるとPCのUSB電源供給能力(500mA?)を越えてしまい、PCのUSBがエラーになるので輝度を絞っています。RGBの最大は255ですが、最大31にしているので1/8位。それでも結構明るくて直視できません。写真はiPhoneのカメラが自動的に絞りを絞っています。

忙しいと言ってはこんなことをしている師走です。

2020年12月5日土曜日

光るクリスマスオーナメント(その4:完成)

今日も朝から雨で寒い一日でした。晴れて暖かくなったらやることがあったのですが、寒かったので部屋で工作ができました。

光るクリスマスオーナメントがようやく完成です。裏表の雪の結晶はタッピングビスで固定することにしました。電池はカプトンテープで電線を貼り付けるだけです。電源は常にオンになりますが、明るいときはsleepしているので消費電力は僅かです。

表側です。

裏側です。

増えてきた観葉植物を越冬のためにリビングに入れたら部屋が狭くなってしまい、今年はツリーを出さないことに。その代わりに観葉植物をイルミネーションやオーナメントで飾り付けることにしました。

ベンジャミンにぶら下げてみたところです。イルミネーションが青色LEDと白色LEDなので、青っぽい絵面になっています。


イルミネーションが消えているとこんな感じです。

動画をアップしました。


電池はクリスマスまでもつ見積もりですが、どうなるでしょうか?


回路図です。

PICのソースです。バグがあるかもしれません。無保証です。著作権は留保しますが、利用や改変などご自由にどうぞ。

;---------------------------------------------------------------------
;  クリスマス・オーナメント
;
;                                                      2020.11.20 naka
;  1. I/Oポート
;
;   (1). 出力ポート
;        GP0-2:  LEDに繋ぐ
;   (2). 入力ポート
;        GP3  : CDSと抵抗でVDDを分圧し明るさをセンス(sleepからの復帰割り込み)
;
;  2. 光り方
;    ・4段階(フル,1/2,1/4,消灯)でランダムに点灯する。
;    ・時々高速に点滅
;    ・時々回転表示
;
;  3. 暗くなると自動点灯
;     普段はsleepしていて、暗くなるとGPIOの変化による割り込みで
;     sleepから復帰して点灯する。
;---------------------------------------------------------------------
	LIST	P=PIC12F675
	INCLUDE	"P12F675.INC"
	__CONFIG _CPD_OFF & _CP_OFF & _BODEN_OFF & _MCLRE_OFF & _PWRTE_OFF & _WDT_OFF & _INTRC_OSC_NOCLKOUT
	ERRORLEVEL	-302	;アセンブル時のバンク警告メッセージ抑制
;---------------------------------------------------------------------
;  マクロ定義
;---------------------------------------------------------------------
BANK0	MACRO
	BCF	STATUS,RP0	;BANK0に切り替える
	ENDM
BANK1	MACRO
	BSF	STATUS,RP0	;BANK1に切り替える
	ENDM
;---------------------------------------------------------------------
;  変数レジスタの定義
;---------------------------------------------------------------------
CNT	EQU	20H	; カウンタ
CNT1	EQU	21H	; WAIT用カウンタ
CNT2	EQU	22H	; WAIT用カウンタ
LED_NO	EQU	23H	; 点灯するLED番号(0~9)
LEDBUF	EQU	24H	; LED表示バッファ(24-25H)
			; 各LED 2ビットで明るさ設定(00,01,10,11の順)
			; 6LEDなので24H、25Hの半分使用
LEDCNT	EQU	26H	; 割り込みで表示するLED番号カウンタ
PATADDR	EQU	27H	; 表示パターンのアドレスカウンタ
PATCNT	EQU	28H	; 表示パターンの表示繰り返しカウンタ
TMP	EQU	29H	; 一時作業用(割り込み内)
FLWAIT	EQU	2AH	; FLASHウエイト時間
PWMCNT	EQU	2BH	; 疑似PWMのためのカウンタ(0~3で変化)
DSPFLG	EQU	2CH	; 表示/非表示フラグ(0:非表示、1:表示)
;
TMP2	EQU	30H	; 一時作業(割り込み外)
RNDVAL	EQU	31H	; 乱数生成用
WK1	EQU	32H	; 乱数生成用
BLNKCNT	EQU	33H	; 高速点滅までのカウンタ
WCNT	EQU	34H	; 高速点滅回数カウンタ
INDEX	EQU	35H	; 回転表示時のパターンインデックス
RCNT	EQU	36H	; 回転表示カウンタ
;
BKUPW	EQU	40H	; 割り込み待避用
BKUPS	EQU	41H	; 割り込み待避用
BKUPFR	EQU	42H	; 割り込み待避用
BKUPPCL	EQU	43H	; 割り込み待避用
;
;---------------------------------------------------------------------
;  リセット・割り込み
;---------------------------------------------------------------------
	ORG	00H		; リセット時の飛び込み先
RESET
	GOTO	START		; 初期設定へ
	;
;---------------------------------------------------------------------
;  割り込み処理でダイナミック表示を行う。
;    (4回の割り込みで1LED分 ; 4段階PWM)
;---------------------------------------------------------------------
	ORG	04H		; 割り込み時の飛び込み先
INTRUPT
	;レジスタ待避
	MOVWF	BKUPW		;バックアップ(Wレジ)
	SWAPF	STATUS,W	;バックアップ(STATUSレジ)
	MOVWF	BKUPS
	MOVF	FSR,W		;バックアップ(FSRレジ)
	MOVWF	BKUPFR
	MOVF	PCLATH,W	;バックアップ(PCLATHレジ)
	MOVWF	BKUPPCL
	;
	BTFSS	INTCON,GPIF	; 外部割り込みか?
	GOTO	INT1
;	MOVF	GPIO,F		; 一度、空読みしないと割り込みが連続する
	BCF	DSPFLG,0
	BTFSC	GPIO,3		; LowならSleep,Highなら表示
	BSF	DSPFLG,0	; 表示
	BCF	INTCON,GPIF
	;
INT1
	;
	BCF	INTCON,T0IF	;割り込みフラグリセット
	;
	; 点灯するLEDの明るさを確認
	;
	MOVF	LEDCNT,W	; LED番号
	CALL	LEDBRT		; 0,1,2,4が返る
	SUBWF	PWMCNT,W	; PWMCNT - 明るさ > 0 なら点灯
	BTFSS	STATUS,C
	GOTO	DISP
	GOTO	UNDISP
	;
DISP
	MOVF	LEDCNT,W
	MOVWF	LED_NO		; 表示番号をLED_NOへ
	;
	; LED表示
	;
	CLRF	GPIO
	BSF	STATUS,RP0	; bank1 切り替え
	MOVLW	H'FF'
	MOVWF	TRISIO
	BCF	STATUS,RP0	; bank0に戻す
	;
	MOVF	LED_NO,W
	CALL	TABLE_TRIS
	BSF	STATUS,RP0	; bank1 切り替え
	MOVWF	TRISIO
	BCF	STATUS,RP0	; bank0に戻す
	MOVF	LED_NO,W
	CALL	TABLE_IO
	MOVWF	GPIO
	;
	GOTO	NEXTBIT
	;
UNDISP
	CLRF	GPIO		; LED非表示
	;
NEXTBIT
	INCF	PWMCNT,W
	ANDLW	H'03'
	MOVWF	PWMCNT
	BTFSS	STATUS,Z
	GOTO	RSTREG
	;
	INCF	LEDCNT,F	; 次の割り込みに備え、カウントアップ
	MOVF	LEDCNT,W
	SUBLW	D'6'		; LED:0~5
	BTFSC	STATUS,Z
	CLRF	LEDCNT
	;
RSTREG
	;レジスタ復元
	MOVF	BKUPPCL,W	;復元(PCLATHレジ)
	MOVWF	PCLATH
	MOVF	BKUPFR,W	;復元(FSRレジ)
	MOVWF	FSR
	SWAPF	BKUPS,W		;復元(STATUSレジ)
	MOVWF	STATUS
	SWAPF	BKUPW,F		;復元(Wレジ)LED_NO
	SWAPF	BKUPW,W
	;
	RETFIE			;割り込みから復帰
	;
;---------------------------------------------------------------------
;  初期設定
;---------------------------------------------------------------------
START
	BCF	INTCON,GIE	; 全割込み禁止
	;
	CLRF	GPIO
	MOVLW	B'00111111'	; GPIOをデジタルI/Oに設定
	MOVWF	CMCON		; デフォルトはコンパレータ
	BANK1
	MOVLW	B'00010111'	; GPIO3,5(空き→出力)
	MOVWF	TRISIO		;
	;TMR0割り込みタイマーセット
	MOVLW	H'88'		; プリスケーラを使わない
	MOVWF	OPTION_REG	; clock4MHz -> 1Mcycle
				; 1Mcycle/256/2=3,906Hz(=256usec毎)
				; 割り込み4回でひとつのLEDを点灯
				; 明るい  :割り込み4回とも点灯
				; 少し暗い:割り込み4回のうち2回のみ点灯
				; 暗い    :割り込み4回のうち1回のみ点灯
				; LED 12個x4回なので3,906/48=81Hzとなりチラチラしない
	; GPIO割り込み(明るさ検知)
	MOVLW	B'00001000' 	; GP3 Interrupt-on-change enabled
	MOVWF	IOC
	BANK0
	;
	CLRF	LEDCNT		; LEDカウンタクリア
	CLRF	LEDBUF		; LEDバッファクリア
	CLRF	LEDBUF+1	; LEDバッファクリア
	CLRF	DSPFLG
	;
	CLRF	PWMCNT
	CLRF	BLNKCNT
	;
;---------------------------------------------------------------------
;  メイン
;---------------------------------------------------------------------
MAIN
	BSF	INTCON,T0IE	; タイマー0割り込みイネーブル
	BSF	INTCON,PEIE	; 周辺割り込み許可
	BSF	INTCON,GPIE	; GPIO change割り込み許可
	BSF	INTCON,GIE	; 割り込み許可
	;
	CALL	DIAG		; 電源投入の動作確認のために表示
	CALL	RANDINT
MAINLP
	CALL	RNDBLINK
	BTFSS	DSPFLG,0	; 非表示ならSleep
	GOTO	MAINSLEEP
	;
	CALL	FASTBLINK
	BTFSS	DSPFLG,0	; 非表示ならSleep
	GOTO	MAINSLEEP
	;
	CALL	RNDBLINK
	BTFSS	DSPFLG,0	; 非表示ならSleep
	GOTO	MAINSLEEP
	;
	CALL	ROTATION
	BTFSS	DSPFLG,0	; 非表示ならSleep
	GOTO	MAINSLEEP
	;
	GOTO	MAINLP
	;
MAINSLEEP
	BCF	INTCON,T0IE	; タイマー0割り込み停止
	CLRF	GPIO
	SLEEP
	NOP
	;
	BSF	INTCON,T0IE	; タイマー0割り込みイネーブル
	GOTO	MAINLP

;---------------------------------------------------------------------
;  高速点滅
;---------------------------------------------------------------------
FASTBLINK
	MOVLW	D'30'
	MOVWF	BLNKCNT
FASTBLP
	CALL	RNDPAT		; ランダム点灯
	MOVLW	D'10'		; wait 40ms
	CALL	WBLINK
	DECFSZ	BLNKCNT,F
	GOTO	FASTBLP
	RETURN
	;
;---------------------------------------------------------------------
;  通常ランダム点滅
;---------------------------------------------------------------------
RNDBLINK
	MOVLW	D'30'
	MOVWF	BLNKCNT
RNDBLP
	CALL	RNDPAT		; ランダム点灯
	MOVLW	D'200'		; wait 800ms
	CALL	WBLINK
	DECFSZ	BLNKCNT,F
	GOTO	RNDBLP
	RETURN
	;
;---------------------------------------------------------------------
;  回転表示(点灯の明るさは1/2)
;---------------------------------------------------------------------
ROTATION
	MOVLW	D'10'		; 10回転
	MOVWF	WCNT
ROTLP1
	MOVLW	D'7'		; 7通りの表示パターン
	MOVWF	RCNT
	CLRF	INDEX
ROTLP2
	CALL	ROTGET
	MOVWF	LEDBUF
	INCF	INDEX,F
	CALL	ROTGET
	MOVWF	LEDBUF+1
	INCF	INDEX,F

	MOVLW	D'10'		; wait時間(40ms)
	CALL	WBLINK
	DECFSZ	RCNT,F
	GOTO	ROTLP2
	;
	DECFSZ	WCNT,F
	GOTO	ROTLP1
	RETURN
	;
ROTGET
	CLRF	PCLATH
	MOVF	INDEX,W
	ADDWF	PCL,F
ROTDT
	RETLW	B'00000011'	; 0
	RETLW	B'00000000'	;
	;
	RETLW	B'00001101'	; 1
	RETLW	B'00000000'	;
	;
	RETLW	B'00110100'	; 2
	RETLW	B'00000000'	;
	;
	RETLW	B'11010000'	; 3
	RETLW	B'00000000'	;
	;
	RETLW	B'01000000'	; 4
	RETLW	B'00000011'	;
	;
	RETLW	B'00000000'	; 5
	RETLW	B'00001101'	;
	;
	RETLW	B'00000000'	; 6
	RETLW	B'00000100'	;
	;
;---------------------------------------------------------------------
;  I/Oパターン
;---------------------------------------------------------------------
TABLE_IO
	CLRF	PCLATH
	ADDWF	PCL,F
	RETLW	B'00000001'	; LED 0
	RETLW	B'00000010'	; LED 1
	RETLW	B'00000010'	; LED 2
	RETLW	B'00000100'	; LED 3
	RETLW	B'00000001'	; LED 4
	RETLW	B'00000100'	; LED 5
	;
TABLE_TRIS
	CLRF	PCLATH
	ADDWF	PCL,F
	RETLW	B'00011100'	; LED 0
	RETLW	B'00011100'	; LED 1
	RETLW	B'00011001'	; LED 2
	RETLW	B'00011001'	; LED 3
	RETLW	B'00011010'	; LED 4
	RETLW	B'00011010'	; LED 5
	;
;---------------------------------------------------------------------
;  Wレジ値をビット位置にデコード
;---------------------------------------------------------------------
DECBIT
	CLRF	PCLATH
	ADDWF	PCL,F
DECBIT1
	RETLW	B'00000001'	;0
	RETLW	B'00000010'	;1
	RETLW	B'00000100'	;2
	RETLW	B'00001000'	;3
	RETLW	B'00010000'	;4
	RETLW	B'00100000'	;5
	RETLW	B'01000000'	;6
	RETLW	B'10000000'	;7
	;
;---------------------------------------------------------------------
;  LED番号からLEDBUFの値(明るさ)を取り出す(0,1,2,4)
;---------------------------------------------------------------------
LEDBRT
	BCF	STATUS,C
	RLF	LEDCNT,W	; LED番号を2倍しジャンプ先アドレスに変換
	CLRF	PCLATH
	ADDWF	PCL,F
	;
	CALL	LEDNO_0
	GOTO	LEDBRT_1
	CALL	LEDNO_1
	GOTO	LEDBRT_1
	CALL	LEDNO_2
	GOTO	LEDBRT_1
	CALL	LEDNO_3
	GOTO	LEDBRT_1
	CALL	LEDNO_4
	GOTO	LEDBRT_1
	CALL	LEDNO_5
	GOTO	LEDBRT_1
LEDBRT_1
	MOVWF	TMP
	SUBLW	D'3'
	BTFSC	STATUS,Z
	INCF	TMP,F
	MOVF	TMP,W
	RETURN
	;
LEDNO_0
	MOVF	LEDBUF,W
	ANDLW	B'00000011'	; LEDNO = 0
	RETURN
	;
LEDNO_1
	MOVF	LEDBUF,W
	ANDLW	B'00001100'	; LEDNO = 1
	MOVWF	TMP
	BCF	STATUS,C
	RRF	TMP,F
	RRF	TMP,W
	RETURN
	;
LEDNO_2
	SWAPF	LEDBUF,W
	ANDLW	B'00000011'	; LEDNO = 2
	RETURN
	;
LEDNO_3
	SWAPF	LEDBUF,W
	ANDLW	B'00001100'	; LEDNO = 3
	MOVWF	TMP
	BCF	STATUS,C
	RRF	TMP,F
	RRF	TMP,W
	RETURN
	;
LEDNO_4
	MOVF	LEDBUF+1,W
	ANDLW	B'00000011'	; LEDNO = 4
	RETURN
	;
LEDNO_5
	MOVF	LEDBUF+1,W
	ANDLW	B'00001100'	; LEDNO = 5
	MOVWF	TMP
	BCF	STATUS,C
	RRF	TMP,F
	RRF	TMP,W
	RETURN
	;
;---------------------------------------------------------------------
;  消灯
;---------------------------------------------------------------------
LEDOFF
	CLRF	LEDBUF
	CLRF	LEDBUF+1
	RETURN
	;
;---------------------------------------------------------------------
;  Waitルーチン(注)割り込みが入るので下記時間にはならない
;---------------------------------------------------------------------
W100US				; 約100usec
	MOVLW	D'32'
	MOVWF	CNT1
W100USLP
	DECFSZ	CNT1,F
	GOTO	W100USLP
	RETURN
	;
WAIT1MS				;1ミリ秒(998usec) CALLと合わせて1.000ms
	MOVLW	D'248'		;1cycle
	MOVWF	CNT1		;1cycle
W1MSLP
	NOP			;1cycle*248
	DECFSZ	CNT1,F		;1cycle*247+2cycle
	GOTO	W1MSLP		;2cycle*247
	GOTO	$+1		;2cycle
	NOP			;1cycle
	RETURN			;2cycle
	;
WBLINK
	; Wレジに待ち時間(単位4ms)を入れてCALL
	MOVWF	CNT2
WBLINKLP
	CALL	WAIT1MS
	CALL	WAIT1MS
	CALL	WAIT1MS
	CALL	WAIT1MS
	BTFSS	DSPFLG,0	; 非表示になったら即時リターン
	RETURN
	DECFSZ	CNT2,F
	GOTO	WBLINKLP
	RETURN
	;
;---------------------------------------------------------------------
;  電源投入時、全LED点灯
;---------------------------------------------------------------------
DIAG
	MOVLW	B'11111111'	;
	MOVWF	LEDBUF
	MOVLW	B'00001111'	;
	MOVWF	LEDBUF+1
	;
	MOVLW	D'250'		; 点灯時間
	CALL	WBLINK
	RETURN
	;
;---------------------------------------------------------------------
;  ランダム表示
;---------------------------------------------------------------------
RNDPAT
	CALL	RANDOM
	MOVWF	LEDBUF
	CALL	RANDOM
	MOVWF	LEDBUF+1
	;
	RETURN
;---------------------------------------------------------------------
;  M系列乱数(1~255)
;---------------------------------------------------------------------
RANDINT
	MOVLW	B'00101010'
	XORWF	TMR0,W
	MOVWF	RNDVAL
RANDOM
	MOVF	RNDVAL,W
	MOVWF	WK1
	RRF	WK1,F
	RRF	WK1,F
	XORWF	WK1,W
	RRF	WK1,F
	RRF	WK1,F
	XORWF	WK1,W
	RRF	WK1,F
	RRF	WK1,F
	RRF	WK1,F
	XORWF	WK1,F
	BCF	STATUS,C
	BTFSC	WK1,0
	BSF	STATUS,C
	RLF	RNDVAL,F
	MOVF	RNDVAL,W
	RETURN
	;
	END
;---------------------------------------------------------------------
;  終わり
;---------------------------------------------------------------------

2020年12月4日金曜日

3Dプリンタの断熱カバー製作

師走に入り寒い日が続いていますが、これが平年並みなのでしょうね。昨日は久しぶりに職場に行きましたが換気のために窓が開けてあり寒く、帰る頃には背中がゾクゾクしてきた。次回はもっと暖かい格好をしていかねば。

さて、ようやく3Dプリンタの断熱カバー作成に着手しました。冬場は室温が10度を下回る(使っていない部屋なので)ため、印刷ノズルやベッドの冷えが気になるので。

使うのは2.5mm厚の養生用プラダンです。200円弱/枚ですが、でかいので運ぶのが大変です。

簡単な図面を書いてサイズを決め、板取り図面を書いて無駄が出ないようにしました。当初、3Dプリンタ全体を覆う積もりでしたが、フィラメントのリールまで含めると背が高くなり過ぎて、狭い部屋での存在感がすごい。そこでフィラメントリールは外に出すように変更しました。

このため印刷中だけ被せるようにカバーするのが難しくなってしまい、常に覆う構造になります。扉も必要になりました。扉のことはまだ未検討。

プラダンはカッタで簡単に切れます。下敷きはダンボールです。

カットが終わりました。左端は60cmの定規です。
結構大きい。

組み立てるための部品を3Dプリンタで印刷しました。立方体の角に取り付けます。手前4つがカバーの天井側、奥4つがカバーの下側です。隙間はプラダンの厚さ2.5mmです。

差し込む箇所のプラダンを少しカットして、隙間ができないようにしました。真ん中の印があるところで90度に折ります。v

仮組みしたところ、ちょっと強度が弱い感じです。プラダンはもう1枚買ってあるので、向きを90度変えて張り合わせようかと思います。

コーナの様子です。

覗き窓用に100均でA3のカードケースを買ってきました。窓をくり抜いてテープで貼り付ける予定です。 

ひとまずここまでですが、現状ではフィラメントリールがカバーに干渉するので、リールの高さを上げる仕掛けも作らねば。

2020年11月29日日曜日

光るクリスマスオーナメント(その3)

まだ師走前ですが忙しくなってしまい工作が余り進まなくなってしまいました。それでも何とかPICを表面実装用のユニバーサル基板に載せました。以前、秋月で購入した基板です。

CDSでの明るさ検知の割り込み用の分圧抵抗は100KΩにしましたが、SMDの手持ちがなかったのでリード部品です。LEDに繋ぐ抵抗はブレッドボードでの評価時は100Ωでしたが、もう少し明るくしたく47ΩのSMDにしました。回路上これがアノードとカソードに繋がるので94Ωです。少し消費電流が多くなり1〜1.5mAくらいです。


細いポリウレタン線で配線したかったのですが、見つからず普通の線材です。LEDはマスキングテープで仮止め中。電池もホルダを使うと厚くなるのでテープで止めてみました。

暗くして点灯を開始した様子です。

明後日から師走です。ぜひ次週には完成させたい(というほど大したものではないが)。今年は街に出るのも憚られるので、少しでもクリスマスの雰囲気を出せればよいかな、と思っています。



2020年11月23日月曜日

久しぶりのPICプログラム

3連休も終わりですが、予定していた工作は殆どできませんでした。家の補修仕事や植木の世話、請け負いのソフト開発仕事をこなしていたらあっという間に連休最終日の夕方です。まぁ暇を持て余すよりは全然よいですが。

クリスマスオーナメント作りは、PICのプログラムを書いた程度です。書いたと言っても
以前作ったUSBクリスマスツリーのプログラムを流用です。PICは8ピンのPIC12F675が見つかったのでこれを使うことにしました。



前回のツリーのようなUSB給電ではなく、CR2032電池を使うので消費電力を下げる必要があります。このため明るいうちはPICをスリープさせておき、暗くなると外部割り込みで復帰するようにしました。VDD(3V)を抵抗とCDSで分圧しておき、明るいうちはLowで、暗くなってCDSの抵抗値が大きくなるとHighになってPICの外部割り込みが起きます。明るくなるとsleepします。

分圧する抵抗が100Kオームのときかなり暗くしないと光りませんでした。47Kオームだとそれほど暗くなくても光り始めます。調整する場合には47Kオームの固定抵抗と半固定抵抗をシリーズに繋げばよさそうです。あるいはCDSに遮光テープを貼るとか。

ただ、明るいときに100Kオームは30μA弱、47Kオームだとこの2倍流れますので消費電力とのトレードオフです。このほかにPICの待機電流もあります。

点灯時の電流は1mA程度でした。LEDを6個使っていますが、ダイナミック点灯なので同時には1個しか点灯しません。点灯していないこともあるので1mAよりは少ないと思います。なお、青色LEDはこの電流では暗かったのでもう少し流した方がよさそうです。

CR2032は公称220mAhの容量なので、1mAだと220Hですね。1日の暗い間14Hだけ光るとすると約15日点灯します。さらに昼間は休んでいるので、多少は電池が復活すると思われます。ということで12月始めに動作させればクリスマスまで持ちそうです。

12月始めに完成するのかという疑問もありますが、、、、

2020年11月19日木曜日

光るクリスマスオーナメント(その2)

前回、雪の結晶の枝は中空にしたのですが、サポート材が邪魔をして先端まで光が届かなかったので、構造を変更して表と裏に分けてみました。これでサポート材なしで印刷できます。ただ、貼り合わせるのが難しそう。

枝が3方向に分かれている場所にLEDを置いてみます。

先端が光りよい感じです。6方向の枝が順次光って回転するようなパターンもよいかな。

使っているLEDは、以前アマゾンで買った安かったセットもの。PICはかなり前に秋月で買って部品箱に入りっぱなしだったPIC16F676のフラットパッケージにしようと思っています。

電池を中に収めるためには、結晶をもう少し厚くする必要がありそうです。また、貼り合わせ方を考えないと。当初、電池は外に出して結晶の表裏は接着剤で貼ればよいかと思ってました。中に入れると電池交換のために何か細工が必要です。

コロナウィルスの感染者が急増しているようなので、今週末の3連休は自宅に籠って工作三昧の予定です。GO TOキャンペーンは何も使わないうちに終わりそう。

2020年11月17日火曜日

光るクリスマスオーナメント

暖かい日が続いていますが、あと2週間で師走です。そろそろクリスマスツリーを準備しようと思い、試しに3Dプリンタでオーナメントを作ってみました。

真ん中が空いているところにPICを載せた基板を入れて6個のLEDで6方向の枝をランダムに点滅させようと思っています。

雪の結晶の腕は光を通すように中空にしましたがサポート材が詰まって、取り除けませんでした。

CR2032電池にφ3mm白色LEDを直結して光の透過具合を確認してみます。かなり明るく写っていますが、最終的には抵抗を入れてPICで制御するのでもう少し暗くなります。

表(反対側)から見るとこんな感じです。

青色LEDで試すとこんな感じです。

やはり結晶の枝先まで光らせるには、枝の中のサポート材をなんとかしないとダメですね。表と裏を別々に作って貼り合わせるかな。

2020年11月15日日曜日

3DプリンタのMicroSDスロット延長

3DプリンタのTRONXY XY2-Proですが、microSDカードを挿すスロットが本体後ろにあってイマイチ使いにくい。さらに印刷完了時には印刷ベッドが一番後ろに退がる(ヘッドが一番前になる)ため、MicroSDスロットがベッドの下になってしまいアクセスしにくくなります。

そこで、MicroSDスロットの延長アダプタを試してみました。写真の様にプラグとソケットがリボンケーブルで繋がっているものです。こんなに延長して電気的に問題ないか心配でしたが、無事に認識しました。


3Dプリンタの前面にあるタッチパネルの下に固定する部品をサクッと作ってみましたが、またまた反ってしまいました。1層目がベッドにしっかり固着しなかったのが原因だと思う。Zゼロ調整が少し高かったかも。まあ、今回は底が反っても問題ないのでそのまま使います。

M4のビス穴はFusion 360で生成しましたが、ちょっとキツくてビスがねじ込めなかったのでタップを立てました。

こんな感じで延長アダプタのMicroSDスロットが入ります。


タッチパネルの空いていたビス穴を使って固定しました。よい感じです。MicroSDカードは撮影の為に奥まで差し込んでません。奥まで差し込むとカチッとしてロックされ、もう一度押し込むとイジェクトされるちゃんとしたソケットでした。

かなり使い勝手が良くなりました。この3Dプリンタは電源スイッチも裏にありますが、これは100均のスイッチ付き節電タップなどを使えば解決できそう。

本体裏からリボンケーブルを伸ばしている様子です。ケーブルの長さがギリギリだったので、もう少し長い方がよかった。浮いてベッドとぶつかりそうなのでマスキングテープで止めてあります。

冬場に向けての保温対策のためにホームセンタでプラダンを買ってきました。大きいですが200円弱です。養生用という2.5mm厚でちょっと薄いですがこれしかなかった。クルマに載るか心配だったのですが、曲げて助手席ドア側と天井を使ってなんとか入りました。


ベッドへのアクセスなどを考えると、印刷を開始したら上から被せる箱のような単純な形にしようかと思っています。終わったら箱を外せば楽にアクセスできるし、扉とか面倒そうなところを作らなくて済むし。使わないときのホコリよけカバーにもなります。

印刷の進捗状況が見えるようにタッチパネルの所だけくり抜いて透明なパネルを貼るつもり。100均で透明な下敷でもゲットしてきます。

ただ、課題がひとつ。3Dプリンタが載っているIKEAのテーブルが小さくてプリンタ全体を覆うような箱が載らない。カッコ悪いけど大きな合板でも敷くかと考え中です。