SSブログ
前の10件 | -

今更Git、GitHub [gitHub]

恥ずかしながら、以下の本を読んで今更勉強しています、ええ!。

わかばちゃんと学ぶ Git使い方入門〈GitHub、Bitbucket、SourceTree〉

わかばちゃんと学ぶ Git使い方入門〈GitHub、Bitbucket、SourceTree〉

  • 出版社/メーカー: シーアンドアール研究所
  • 発売日: 2017/04/21
  • メディア: 単行本(ソフトカバー)



読んで、いや読みながら自分なりにまとめてています。
https://github.com/hamayanShowa-ele/IntroductionToGit

https://hamayanshowa-ele.github.io/IntroductionToGit/

是非参加してください。本を読んだとしても、実際に自分で運用・管理しないと実感できないのですね。

んが!詳細や挿絵は無しなので上記本を含めてちゃんとした本を買って読んだ方がイイですよ!

あと、こんなん公開しています。
https://github.com/chobichan

nice!(0)  コメント(2) 

ESP8266への気付き [ESP32]

esp8266_io0.pngESP8266のIO0って、出力設定しないと暴れているよね!なんで???

Deep Sleep!
ESP.deepSleep( 100 * 1000 * 1000UL , WAKE_RF_DEFAULT );
で省電力に入った場合、指定した時間の間だけ省電力で、その後タイムアウトすると8mAくらい消費している感じ。

Light Sleep!
light sleepモードは1mA程度に抑えられるのは助かる。んが、結構手続きが面倒かも。あと起床する手段が必要だが外部割込みが使える。以下のサイトがよく判る。
http://okiraku-camera.tokyo/blog/?p=4996

以下のリンク先のPDFのサンプルをそのまま使っても、ただしくlight sleep modeに入らないのがムカつくよね。タイムアウトで抜ける処理をやりたいのに、、、
https://www.espressif.com/sites/default/files/documentation/2c-esp8266_non_os_sdk_api_reference_en.pdf

http://okiraku-camera.tokyo/blog/?p=4996
上記リンクのサンプルを若干アレンジして試してみたのだが、かなりの頻度でlight sleepから抜けてしまう現象が発生していた。またコールバックルーチンがいきなり呼ばれてしまう、、、
割込み入力端子はプルアップ抵抗とスイッチのみ接続し、GNDに落とすトリガーを入れている。
なぜいきなり省電力から抜けるのか結局判らない。ただし対策としてコールバックルーチンの中で端子状態を確認し、GNDレベルでなければ再度light sleepに入る様に対策して、確実に省電力状態に入る様にした。


以下、サンプルコード
/*
 ESP8266 light sleep test. 

  気づき
  1.ESP8266をlight sleepに入れると、タイマー系は全て停止してしまうので、
     sleepに入っていた時間を知る事ができない。
  2.wifi再接続時は13秒とか掛かる事が多い。
  3.なぜか一発でlight sleepに入らない事も多い。
  4.上記は、light sleepに入った途端にコールバックルーチンが呼ばれてしまう様で、ポートで端子状態を確認が必要。
  5.コールバックルーチンの中でdelayとかyieldとか呼ぶと、コアを吐いて死んでしまう。

  参考
  http://okiraku-camera.tokyo/blog/?p=4996
*/
#include  <ESP8266WiFi.h>
extern "C" {
  #include "user_interface.h"
  #include "gpio.h"
}

/**********************************************************/
/* defines                                                */
/**********************************************************/
#define  ASSOC         0
#define  INT_X         2
#define  GPS_POWER_PIN 15

#define  FPM_SLEEP_MAX_TIME       0xFFFFFFF

/**********************************************************/
/* global variables                                       */
/**********************************************************/
bool lightSleepLoop;

/**********************************************************/
/* setup.                                                 */
/**********************************************************/
void setup()
{
  Serial.begin( 115200 );
  delay( 20UL );
  Serial.println();
  Serial.println( "ESP8266 light sleep test." );

  /* GPIOの初期化。消費電力を抑える目的でもある */
  pinMode( ASSOC, OUTPUT ); digitalWrite( ASSOC, LOW ); /* active high */
  pinMode( INT_X, INPUT_PULLUP ); /* active low input */
  pinMode( GPS_POWER_PIN, OUTPUT ); digitalWrite( GPS_POWER_PIN, LOW ); /* active high */

  /* enter light sleep mode. */
  enterLightSleep( FPM_SLEEP_MAX_TIME );
}

/**********************************************************/
/* loop.                                                  */
/**********************************************************/
void loop()
{
  delay( 500 );
  digitalWrite( ASSOC, (digitalRead( ASSOC ) == LOW) ? HIGH : LOW );
}

/**********************************************************/
/* call back routine from wake up.                        */
/**********************************************************/
void fpm_wakup_cb( void )
{
  if( digitalRead( INT_X ) == LOW && digitalRead( INT_X ) == LOW && digitalRead( INT_X ) == LOW )
    lightSleepLoop = false;
  Serial.write( 'c' );
}
 
/**********************************************************/
/* enter the light sleep mode.                            */
/**********************************************************/
void enterLightSleep( uint32_t period )
{
  WiFi.mode( WIFI_OFF );
  wifi_set_opmode_current( NULL_MODE ); 
  wifi_fpm_set_sleep_type( LIGHT_SLEEP_T ); 
  wifi_fpm_open();
  gpio_pin_wakeup_enable( INT_X, GPIO_PIN_INTR_LOLEVEL );  /* or GPIO_PIN_INTR_HILEVEL */
  wifi_fpm_set_wakeup_cb( fpm_wakup_cb ); // Set wakeup callback
  lightSleepLoop = true;
  do
  {
    wifi_fpm_do_sleep( period ); // sleep until gpio activity.
    delay( 50UL );
    Serial.write( '*' );
  } while( lightSleepLoop );
  gpio_pin_wakeup_disable();
  wifi_fpm_close(); // disable force sleep function
}

light sleep時のタイマー
うすうすはそうじゃないか?とは思っていたが、しかしwifi_fpm_do_sleepの引数はμs単位の時間を指定できる事もあり、何度かタイムアウトで省電力から抜けないか?と試したが、少なくともESP-WROOM-02ではできなかった。
light sleep時はタイマー自体が止まってしまうようで、他のマイコンでよくある低消費電力モードに入ってもRCオシレータは動いている!という事は無いのかもしれない。
この為、ESP8266単体で省電力に入っていた時間を知る事はできないのかもしれない。復帰した時にNTPなどに接続すればイイのだが、、、イチイチそんな目的の為にNTPに接続するのもねぇ!
※micros()で省電力に入る直前、直後の時間を出力しても、省電力の時間分進んでいる事は無かった。

3.3Vレギュレータ!
ESP8266とは関係無い?が、3.3Vレギュレータはこれを使った。
無負荷時電流が小さいのが助かる。
http://akizukidenshi.com/catalog/g/gI-11299/


ESP8266_TOUT_ADC.pngTOUT!
TOUTを使って電圧測定をしてみた。その前に、TOUTのADCは10bitのADCであり、入力範囲は0V~1Vであるらしい。
プロットさせてみたのが左のグラフ。簡易的にデータを取った為かどうか判らないが、直線ではない。でも小数点以下1桁くらいの精度の電圧測定なら十分な気もする。これって、内部で補正されているんだっけ?
※Vinは分圧してからTOUTに接続している。



ソフトウエアシリアルのバッファサイズを変更する!
なんであんなケチケチバッファを割り当てているねん?

SoftwareSerial gpsSerial( GPS_TXD, GPS_RXD ); // RX, TX
gpsSerial.begin( 9600, SWSERIAL_8N1, GPS_TXD, GPS_RXD, false , 4096, 4096 );
// 最後から二番目はバッファサイズ、最後は割込み時のバッファサイズ。大きなデータを受信する時は大きめに!

nice!(0)  コメント(0) 

Arduinoライブラリでテレビジョンを制御 家庭でできるIoT! 5パルス目 [ESP32]

2019-12-13 09.13.34.png2019-12-13 09.13.27.png前回( https://hamayan.blog.ss-blog.jp/2019-12-09-1 )作成したリモコンのコードを受信するプログラムを使って、テレビジョン(AQUOS)の幾つかのリモコンコードをサンプリングしました。その内容を使ってBlynkでリモコンを実現します。
もうお判りでしょうが、AQUOSライブラリを作成するにあたってHeatpumpIR Classを使って赤外線ドライバー部分はお手軽に実現しています。
テレビはNHKとテレ東くらいしか観ないので、Blynkのパネルの一番下に一発選局ボタンを付けておきました(笑)。
もうちょい機能を追加すれば、テレビのリモコン無くしても安心ですね!

aquos.h
/****************************************************************************/
/* お家のAQUOSをなんとかするヘッダー                                            */
/*                         Copyright (C) 2014 hamayan All Rights Reserved.  */
/****************************************************************************/
#ifndef aquos_h
#define aquos_h

#include  <Arduino.h>
#include  <HeatpumpIR.h>

extern "C" {
//  #include  <mul_tsk.h>
}

/****************************************************************************/
/* なにかの定義                                                             */
/****************************************************************************/
#define  AEHA_T_PERIOD  425UL  //
#define  AEHA_REPEAT_PERIOD  100UL  //

typedef struct  /*AQUOSのフォーマットはAEHAタイプ*/
{
  uint8_t customerCode[2];
  uint8_t data[4];
} AQUOS_CODE;


class aquos : public HeatpumpIR
{
private:
  int antenaSelect;
  int channelSelect;
  uint32_t startTim;
  void send( IRSender& IR, const uint8_t *code, size_t size );

public:
  aquos();
  void onOff( IRSender& IR );
  void ch( IRSender& IR, int value );
  void chUpDown( IRSender& IR, int value );
  void volume( IRSender& IR, int value );
  void antena( IRSender& IR );
  void decide( IRSender& IR );
  void back( IRSender& IR );
  void program( IRSender& IR );
  void recorderList( IRSender& IR );
  void selectorSwitch( IRSender& IR, int upDown, int rightLeft );
  void favorite( IRSender& IR, int sel );
  char type( void );
};


#endif  /*aquos_h*/

/****************************************************************************/
/*                         Copyright (C) 2014 hamayan All Rights Reserved.  */
/****************************************************************************/


aquos.cpp
/*********************************************************************************/
/* お家のAQUOSをなんとかするソース                                                   */
/*                                         designed by hamayan since 2015/05/28  */
/*********************************************************************************/
#include  "aquos.h"

#if !defined( _MULTITASK_H_ )
#define  dly_tsk(tim)  delay(tim)
#define  rot_rdq()
#define  loc_cpu()     interrupts()
#define  unl_cpu()     noInterrupts()
#endif /*_MULTITASK_H_ */

/*************************************************************************/
/* 大域変数宣言                                                          */
/*************************************************************************/

/*************************************************************************/
/* プロトタイプ宣言                                                      */
/*************************************************************************/

/*************************************************************************/
/* インスタンス                                                          */
/*************************************************************************/
aquos::aquos()
{
  antenaSelect = 0;
  channelSelect = 1;
}

/*************************************************************************/
/* send                                                                  */
/*************************************************************************/
void aquos::send( IRSender& IR, const uint8_t *code, size_t size )
{
  uint8_t tempUC[ size ];
  memcpy( tempUC, code, size );

  // 40 kHz PWM frequency
  IR.setFrequency( 38 );

  //リピートでメソッドが呼ばれた時、強制的に間隔を開ける
  while( (millis() - startTim) < AEHA_REPEAT_PERIOD ) delay( 10 );
  // start time
  startTim = millis();

  // Header
  IR.mark( 8 * AEHA_T_PERIOD );  //frame ppm on
  IR.space( 4 * AEHA_T_PERIOD );  //frame ppm off

  // Data
  for( unsigned int i = 0; i < size; i++ )
  {
    IR.sendIRbyte( tempUC[i], AEHA_T_PERIOD, AEHA_T_PERIOD, 3 * AEHA_T_PERIOD );  // data,426us,425us,1275us
    // pulse on period, pulse off period at ZERO, pulse off period at 1
  }

  // End mark
  IR.mark( AEHA_T_PERIOD );
  IR.space( 0 );
}

/*************************************************************************/
/* 番組表                                                                */
/*************************************************************************/
static const uint8_t TVPROGRAMLIST[]   = {0xaa,0x5a,0x8f,0x12,0x60,0xf2};

void aquos::program( IRSender& IR )
{
  send( IR, TVPROGRAMLIST, sizeof( TVPROGRAMLIST ) );
}

/*************************************************************************/
/* 電源の入り切。だが実際にはテレビがそれに追従してはいない。            */
/*************************************************************************/
static const uint8_t TVOnOff[] = {0xaa,0x5a,0x8f,0x12,0x16,0xd1};

void aquos::onOff( IRSender& IR )
{
  send( IR, TVOnOff, sizeof( TVOnOff ) );
}

/*************************************************************************/
/* 決定ボタン                                                            */
/*************************************************************************/
static const uint8_t DECIDE[] = {0xaa,0x5a,0x8f,0x12,0x52,0xd1};

void aquos::decide( IRSender& IR )
{
  send( IR, DECIDE, sizeof( DECIDE ) );
}

/*************************************************************************/
/* 戻るボタン                                                            */
/*************************************************************************/
static const uint8_t BACK[] = {0xaa,0x5a,0x8f,0x12,0xe4,0x01};

void aquos::back( IRSender& IR )
{
  send( IR, BACK, sizeof( BACK ) );
}

/*************************************************************************/
/* 録画リスト                                                            */
/*************************************************************************/
static const uint8_t RECORDERLIST[]   = {0xaa,0x5a,0x8f,0x12,0xad,0x3f};

void aquos::recorderList( IRSender& IR )
{
  send( IR, RECORDERLIST, sizeof( RECORDERLIST ) );
}

/*************************************************************************/
/* 選択用スイッチ                                                        */
/*************************************************************************/
static const uint8_t SELECTORSWITCH[][6] =
{
  {0xaa,0x5a,0x8f,0x12,0x57,0x81},  /*up*/
  {0xaa,0x5a,0x8f,0x12,0x20,0x81},  /*down*/
  {0xaa,0x5a,0x8f,0x12,0xd8,0xf1},  /*right*/
  {0xaa,0x5a,0x8f,0x12,0xd7,0x01},  /*left*/
};

void aquos::selectorSwitch( IRSender& IR, int upDown, int rightLeft )
{
  if( upDown >= 1 )
    send( IR, SELECTORSWITCH[0], sizeof( SELECTORSWITCH[0] ) );
  else if( upDown <= -1 )
    send( IR, SELECTORSWITCH[1], sizeof( SELECTORSWITCH[1] ) );

  dly_tsk( 100UL );

  if( rightLeft >= 1 )
    send( IR, SELECTORSWITCH[2], sizeof( SELECTORSWITCH[2] ) );
  else if( rightLeft <= -1 )
    send( IR, SELECTORSWITCH[3], sizeof( SELECTORSWITCH[3] ) );
}

/*************************************************************************/
/* チャネルの切り替え                                                    */
/*************************************************************************/
static const uint8_t TVCH[][6] =
{
  {0xaa,0x5a,0x8f,0x12,0x4e,0x32},  /*ch1*/
  {0xaa,0x5a,0x8f,0x12,0x4f,0x22},  /*ch2*/
  {0xaa,0x5a,0x8f,0x12,0x50,0xc2},  /*ch3*/
  {0xaa,0x5a,0x8f,0x12,0x51,0xd2},  /*ch4*/
  {0xaa,0x5a,0x8f,0x12,0x52,0xe2},  /*ch5*/
  {0xaa,0x5a,0x8f,0x12,0x53,0xf2},  /*ch6*/
  {0xaa,0x5a,0x8f,0x12,0x54,0x82},  /*ch7*/
  {0xaa,0x5a,0x8f,0x12,0x55,0x92},  /*ch8*/
  {0xaa,0x5a,0x8f,0x12,0x56,0xa2},  /*ch9*/
  {0xaa,0x5a,0x8f,0x12,0x57,0xb2},  /*ch10*/
  {0xaa,0x5a,0x8f,0x12,0x58,0x42},  /*ch11*/
  {0xaa,0x5a,0x8f,0x12,0x59,0x52},  /*ch12*/
};

void aquos::ch( IRSender& IR, int value )
{
  if( value >= 1 && value <= 12 )
  {
    send( IR, TVCH[value - 1], sizeof( TVCH[0] ) );
    channelSelect = value;
  }
}

/*************************************************************************/
/* チャネルの上下                                                        */
/*************************************************************************/
static const uint8_t TVSEL[][6] =
{
  {0xaa,0x5a,0x8f,0x12,0x11,0xa1},  /*decide up*/
  {0xaa,0x5a,0x8f,0x12,0x12,0x91},  /*decide down*/
};

void aquos::chUpDown( IRSender& IR, int value )
{
  if( value > 0 )  //up
  {
    send( IR, TVSEL[0], sizeof( TVSEL[0] ) );
    channelSelect++;
  }
  else if( value < 0 )  //down
  {
    send( IR, TVSEL[1], sizeof( TVSEL[1] ) );
    channelSelect--;
  }
}

/*************************************************************************/
/* チューナー(アンテナ)の切り替え                                        */
/*************************************************************************/
static const uint8_t TVANTENA[][6] =
{
  {0xaa,0x5a,0x8f,0x12,0x89,0x82},  /*地デジ*/
  {0xaa,0x5a,0x8f,0x12,0x8a,0xb2},  /*BS*/
  {0xaa,0x5a,0x8f,0x12,0x8b,0xa2},  /*CS*/
};

void aquos::antena( IRSender& IR )
{
  if( ++antenaSelect > 1 ) antenaSelect = 0;
  
  send( IR, TVANTENA[antenaSelect], sizeof( TVANTENA[0] ) );
}

/*************************************************************************/
/* ボリュームの上下                                                      */
/*************************************************************************/
static const uint8_t TVVOLUME[][6] =
{
  {0xaa,0x5a,0x8f,0x12,0x14,0xf1},  /*volume up*/
  {0xaa,0x5a,0x8f,0x12,0x15,0xe1},  /*volume down*/
};

void aquos::volume( IRSender& IR, int value )
{
  if( value < 0 )
  {
    send( IR, TVVOLUME[1], sizeof( TVVOLUME[1] ) );
  }
  else if( value > 0 )
  {
    send( IR, TVVOLUME[0], sizeof( TVVOLUME[0] ) );
  }
}

/*************************************************************************/
/* お気に入り番組登録                                       */
/*************************************************************************/
void aquos::favorite( IRSender& IR, int value )
{
  if( value == 0 )  /* nhk sougou. */
  {
    antenaSelect = 0;
    channelSelect = 1;
  }
  else if( value == 1 )  /* nhk bs premium. */
  {
    antenaSelect = 1;
    channelSelect = 3;
  }
  else if( value == 2 )  /* tokyo televison. */
  {
    antenaSelect = 0;
    channelSelect = 7;
  }
  else if( value == 3 )  /* bs tokyo televison. */
  {
    antenaSelect = 1;
    channelSelect = 7;
  }
  else return;

  send( IR, TVANTENA[antenaSelect], sizeof( TVANTENA[0] ) );
  send( IR, TVCH[channelSelect - 1], sizeof( TVCH[0] ) );
}

/*************************************************************************/
/* リモコンフォーマットを答える                                          */
/*************************************************************************/
char aquos::type( void )
{
  return 'A';  /*AEHAのA!*/
}


/*********************************************************************************/
/* end of file                                                                   */
/*                                         designed by hamayan since 2015/05/28  */
/*********************************************************************************/


スケッチの追加部分のみ
aquos  *tv = new aquos();

/*************************************************************************/
/*  blynk virtual pin 6 function as TV power on or off.                  */
/*************************************************************************/
BLYNK_WRITE( V6 )
{ 
  tv->onOff( irSender );
}

/*************************************************************************/
/*  blynk virtual pin 7 function as TV program list on or off.           */
/*************************************************************************/
BLYNK_WRITE( V7 )
{ 
  tv->program( irSender );
}

/*************************************************************************/
/*  blynk virtual pin 8 function as TV record list on or off.            */
/*************************************************************************/
BLYNK_WRITE( V8 )
{ 
  tv->recorderList( irSender );
}

/*************************************************************************/
/*  blynk virtual pin 9 function as TV antena(tuner) change.            */
/*************************************************************************/
BLYNK_WRITE( V9 )
{ 
  tv->antena( irSender );
}

/*************************************************************************/
/*  blynk virtual pin 10 function as channel NHK SOUGOU.                 */
/*************************************************************************/
BLYNK_WRITE( V10 )
{ 
  tv->favorite( irSender, 0 );
}

/*************************************************************************/
/*  blynk virtual pin 11 function as channel NHK BS PREMIUM.             */
/*************************************************************************/
BLYNK_WRITE( V11 )
{ 
  tv->favorite( irSender, 1 );
}

/*************************************************************************/
/*  blynk virtual pin 12 function as channel TOKYO TELEVISION.           */
/*************************************************************************/
BLYNK_WRITE( V12 )
{ 
  tv->favorite( irSender, 2 );
}

/*************************************************************************/
/*  blynk virtual pin 13 function as channel BS TOKYO TELEVISION.        */
/*************************************************************************/
BLYNK_WRITE( V13 )
{ 
  tv->favorite( irSender, 3 );
}

/*************************************************************************/
/*  blynk virtual pin 14 function as cursor move.                        */
/*************************************************************************/
BLYNK_WRITE( V14 )
{ 
  int rightLeft = param[0].asInt();
  int upDown    = param[1].asInt();
  tv->selectorSwitch( irSender, upDown, rightLeft );
}

/*************************************************************************/
/*  blynk virtual pin 15 function as channel up.                         */
/*************************************************************************/
BLYNK_WRITE( V15 )
{ 
  int channel = param.asInt();
  tv->chUpDown( irSender, channel );
}

/*************************************************************************/
/*  blynk virtual pin 16 function as channel down.                       */
/*************************************************************************/
BLYNK_WRITE( V16 )
{ 
  int channel = param.asInt();
  tv->chUpDown( irSender, channel );
}

/*************************************************************************/
/*  blynk virtual pin 17 function as volume up.                          */
/*************************************************************************/
BLYNK_WRITE( V17 )
{ 
  int volume = param.asInt();
  tv->volume( irSender, volume );
}

/*************************************************************************/
/*  blynk virtual pin 18 function as volume down.                        */
/*************************************************************************/
BLYNK_WRITE( V18 )
{ 
  int volume = param.asInt();
  tv->volume( irSender, volume );
}

/*************************************************************************/
/*  blynk virtual pin 19 function as decide button.                      */
/*************************************************************************/
BLYNK_WRITE( V19 )
{ 
  tv->decide( irSender );
}

/*************************************************************************/
/*  blynk virtual pin 20 function as back button.                        */
/*************************************************************************/
BLYNK_WRITE( V20 )
{ 
  tv->back( irSender );
}



ESP-WROOM-02開発ボード

ESP-WROOM-02開発ボード

  • 出版社/メーカー: スイッチサイエンス(Switch Science)
  • メディア: おもちゃ&ホビー









nice!(0)  コメント(0) 

TDK-Lamda製DC/DC電源 RDS50-11-5の中身 [電子工作]

TDK-Lamda製DC/DC電源 RDS50-11-5がノイズ試験で飛んだ!らしいので、折角なので中身を見てみた。

RDS50-11-5の製品ページ
https://product.tdk.com/ja/search/power/switching-power/dc-dc-converter/info?part_no=RDS50-110-5

2019-12-12 10.08.08.jpg一番右のシリコンが付いているモジュールは、同期スイッチのモジュールか?そこそこ大きなヒートシンクが付いていた。
あちこちに貫通ビアを使って、パターンの強化を行っている。
端子台右となりの青いのはヒューズ。DC450V 3.15A仕様。
青いヒューズの下はバリスター。
基板中央と右下のノーマルモードノイズ用のフイルムコンデンサは、ポリミイドテープで結構がっちり保護されている。何対策?
コモンモード用の磁器コンデンサは、入力端子台直後と、スイッチングモジュール直前、スイッチングモジュール直後に入っている。
スイッチングモジュールの左隣の白いのは温度ヒューズだと思う。
温度ヒューズの左隣はコモンモードノイズフィルター。コモンモードノイズフィルターの二次側に逆接防止用のダイオードが入っている。
温度ヒューズの下はFET、おそらく何かあった時に電源を切るのだと思う。
コモンモードノイズフィルターの上のトランスは、フィードバック回路用の電源を作るためのもの。
トランスの左隣は出力センス回路の入力フィルターの様だ。


2019-12-12 10.08.43.jpg半田面側はこんな感じ。右側がスイッチング回路。黄土色はフィードバック用のフォトカプラ。
部品面、半田面共にコーティングがされている。基板上の貫通している穴はすべてマスキングしてコーティングの液体が流れないようにした形跡有り。


2019-12-12 10.13.24.jpgこんな端子台有るんだ!特注?
基板の下側のシャーシは、全面的にマイラフィルムを貼っている。




nice!(0)  コメント(0) 

Arduinoで赤外線リモコンコードを読み出す 家庭でできるIoT! 4パルス目 [ESP32]

odelic_seen_01.png前回ちらっと書いたリモコンコードの受信です。

秋月電子で売られている赤外線受光素子( http://akizukidenshi.com/catalog/g/gI-04659/ )をデータシート通りに抵抗、コンデンサ等を接続し、OUT端子をESP8266に接続します。
この受光素子はサブキャリアを検出するとLowを出力、サブキャリアが無ければHigh(HI-Z)です。
ESP8266側では端子割込みを使ってLowやHighの時間を測定し、時間をビットに換算します。
比較的簡単な処理ですが、悩むのがフレームの終了検出で、NECフォーマットであればビット数は固定長ですが、AEHAフォーマットの場合はビット数は可変長となるので、どこまで取り込んで良いのか判りません。
ですがフレーム間の間隔は必ず空く仕様なので、時間でフレームの終端を強制的に決めてしまう事としました。
とは言え三菱のエアコンのリモコンコードはデータ部が18byte有り、もし全ビットが1であればフレーム長は250msを超えてしますので、いかがなものか?と思わないでもないですが、、、

まぁリモコンコードを見るだけの処理なので、細かい事は気にしません。


aquos_odelic_power_on_off.png実際にスケッチを組んで読み出したものです。オーデリックのリモコンとアクオスのリモコンを受信しています。
オーデリックは前出の様にNECフォーマットです。アクオスはAEHAフォーマットでした。このリモコンコードはメーカーが仕様で示したものではなく、あくまでも自分で解析したコードである事に十分注意して下さい。仮に他のプログラムでこのコードを送信しても上手くは行かないと思います。学習リモコンの様にする場合は、受信したコードを送信可能なプログラムが別途必要です。なお、前回のプログラムでは使えます。


#include  <ESP8266WiFi.h>

void ICACHE_RAM_ATTR irRemocon_interrupt( void );

#define  IR_CONT  12 /* Ir LED ON/OFF */
#define  IR_RECV  13 /* Ir receive */

#define  Max_IR_Bits_NEC  32
#define  Max_IR_Bits_AEHA 256

#define  NEC_HEADER_LOW_MIN_PERIOD  462UL
#define  NEC_HEADER_LOW_MAX_PERIOD  662UL

#define  AEHA_HEADER_LOW_MIN_PERIOD 350UL
#define  AEHA_HEADER_LOW_MAX_PERIOD 500UL

unsigned long IR_Low_Time;    // holds IR bursting time
unsigned long IR_High_Time;   // holds IR high idling time
unsigned long Latest_Time_ms;
unsigned short IR_Data[Max_IR_Bits_AEHA] = {0,};  // holds the bit length of each data bit by micro-sec.
int IR_State;
int IR_Bit_Count;
bool IR_Active = false;  // when true, the capturing IR data stream is valid
char IR_formatType;

void setup()
{
  Serial.begin( 115200UL );
  Serial.println( "\r\n\r\nIR recieve code print out." );
  pinMode( IR_RECV, INPUT_PULLUP );
  attachInterrupt( IR_RECV, irRemocon_interrupt, CHANGE );
}

void loop()
{
  if( IR_Active )
  {
    if( IR_formatType == 'N' && (millis() - Latest_Time_ms) >= 18UL )
    {
      if( irRemocon_available() >= 4 ) IR_nec();
      irRemocon_init();
    }
    else if( IR_formatType == 'A' && (millis() - Latest_Time_ms) >= 10UL )
    {
      if( irRemocon_available() >= 6 ) IR_aeha();
      irRemocon_init();
    }
  }
}

/*************************************************************************/
/* int to hexadecimal                                                    */
/*************************************************************************/
char *itoh( char *dst, unsigned int value )
{
  sprintf( dst, "%02x", value );
  
  return dst;
}

/*************************************************************************/
/* IR_REMOCON初期化                                                      */
/*************************************************************************/
void irRemocon_init( void )
{
  IR_Active = false;       // when 1, the capturing IR data stream is valid
  IR_formatType = 0;
  IR_State = 0;
}

/*************************************************************************/
/* データ数確認                                                          */
/*************************************************************************/
int irRemocon_available( void )
{
  if( IR_Active ) return IR_State / 8;

  return 0;
}

/*************************************************************************/
/* NECタイプのデータ受信                                                 */
/*************************************************************************/
int irRemocon_rxNEC( byte dst[], int size )
{
  int index = 0;

  for( int i = 0; i < Max_IR_Bits_NEC; i++ )
  {
    dst[index] >>= 1;
    if ( IR_Data[i] > ((NEC_HEADER_LOW_MIN_PERIOD + NEC_HEADER_LOW_MAX_PERIOD) / 2) * 3 )
      dst[index] |= 0x80;
    if( (i % 8) == 7 ) index++;
  }

  return index;
}

/*************************************************************************/
/* AEHAタイプのデータ受信                                                */
/*************************************************************************/
int irRemocon_rxAEHA( byte dst[], int size )
{
  volatile int limit = IR_State;
  int index = 0;

  for( int i = 0; i < limit; i++ )
  {
    dst[index] >>= 1;
    if ( IR_Data[i] > ((AEHA_HEADER_LOW_MIN_PERIOD + AEHA_HEADER_LOW_MAX_PERIOD) / 2) * 3 )
      dst[index] |= 0x80;
    if( (i % 8) == 7 ) index++;
  }

  return index;
}

/****************************************************************************/
/* IR RECEIVE for NEC format                                                */
/****************************************************************************/
void IR_nec( void )
{
  int sz = irRemocon_available();
  byte *iRData = new byte[sz];
  sz = irRemocon_rxNEC( iRData, sz );

  String str = "";
  char asc[8];
  for(int i = 0; i < sz; i++)
  {
    str += itoh(asc,(unsigned int)iRData[i]);
    str += ",";
  }
  delete[] iRData;

  if( str.length() > 0 )
  {
    Serial.print( "type NEC:sz = " ); Serial.print( sz, DEC );
    Serial.print( " code:" ); Serial.println( str );
  }
}

/****************************************************************************/
/* IR RECEIVE for AEHA format                                               */
/****************************************************************************/
void IR_aeha( void )
{
  int sz = irRemocon_available();
  byte *iRData = new byte[sz];
  sz = irRemocon_rxAEHA( iRData, sz );

  char buf[8];
  String str = "";
  for(int i = 0; i < sz; i++)
  {
    str += itoh(buf,(unsigned int)iRData[i]);
    str += ",";
  }
  delete[] iRData;

  if( str.length() > 0 )
  {
    Serial.print( "type AEHA: sz=" ); Serial.print( sz, DEC );
    Serial.print( " code:" ); Serial.println( str );
  }
}

/*************************************************************************/
/* IR_REMOCON割り込み                                                    */
/*************************************************************************/
void ICACHE_RAM_ATTR irRemocon_interrupt( void )
{
  if( digitalRead( IR_RECV ) == LOW )  // edge is low.
  {
    if( IR_Active )  //LOWからLOWまでの時間を計測して配列に保存する
    {
      unsigned long period = micros() - IR_Low_Time;

      if( IR_formatType == 'N' &&
        period >= NEC_HEADER_LOW_MIN_PERIOD * 2 && period <= NEC_HEADER_LOW_MAX_PERIOD * 4 )
      {
        if( IR_State < Max_IR_Bits_NEC )
        {
          IR_Data[IR_State++] = (unsigned short)period;
          IR_Bit_Count = IR_State;
          Latest_Time_ms = millis();
        }
        else irRemocon_init();
      }
      else if( IR_formatType == 'A' &&
        period >= AEHA_HEADER_LOW_MIN_PERIOD * 2 && period <= AEHA_HEADER_LOW_MAX_PERIOD * 4 )
      {
        if( IR_State < Max_IR_Bits_AEHA )
        {
          IR_Data[IR_State++] = (unsigned short)period;
          IR_Bit_Count = IR_State;
          Latest_Time_ms = millis();
        }
        else irRemocon_init();
      }
    }
    IR_Low_Time = micros();
  }
  else  // edge is high.
  {
    unsigned long period = micros() - IR_Low_Time;

    if( period >= NEC_HEADER_LOW_MIN_PERIOD * 8 )  // NECフォーマット
    {
      IR_Active = true;
      IR_State = 0;
      IR_High_Time = millis();  //先頭フレームの後半の時間となる
      IR_formatType = 'N';
      Latest_Time_ms = millis();
    }
    else if( period >= AEHA_HEADER_LOW_MIN_PERIOD * 8 )  // 家製協(AEHA)フォーマット
    {
      IR_Active = true;
      IR_State = 0;
      IR_High_Time = millis();  //先頭フレームの後半の時間となる
      IR_formatType = 'A';
      Latest_Time_ms = millis();
    }
    else {}
  }
}




ESP-WROOM-02開発ボード

ESP-WROOM-02開発ボード

  • 出版社/メーカー: スイッチサイエンス(Switch Science)
  • メディア: おもちゃ&ホビー









nice!(0)  コメント(0) 

Arduinoライブラリで天井照明を制御 家庭でできるIoT! 3パルス目 [ESP32]





2019-12-09 23.47.55.png誰も期待していないでしょうけれど、Blynkで動かす。

今回のネタは天井照明です。家にはLEDタイプのオーデリックの天井照明を全部の部屋に入れました。
アマゾンリンクの前の型ですが、多分リモコン制御に関しては変わらないと思います。
この天井照明は結構コストパフォーマンスに優れていると思います。12畳でリモコン付き、調光付きで実売が1万円切っていますから。家に入れたのは2015年3月ですので、4年以上安定稼働しています。またわざわざIoTにしなくても付属のリモコンに時計が付いていますので、指定時間に照明を消す/点けるができます。


さて、この天井照明をリモコンで制御するためにはリモコンコードを知らなくてはできませんが(※相変わらずリモコンコードの仕様は公開されていない)、、、とは言え秋月電子とかで売られている赤外線受光素子を用意すれば( http://akizukidenshi.com/catalog/g/gI-04659/ )、Arduinoを使って比較的容易にコードを読み取る事ができます。※受光についてはその内やります。
とりあえずこの天井照明はNECフォーマットとなっています。

一般的に家電に使われているリモコンフォーマットは、NECフォーマット、AEHAフォーマット、SONYフォーマットが有ります。これらのフォーマットの詳細については以下のページを読んで下さい。
ChaNさんのリモコンコードの解説ページ
http://elm-chan.org/docs/ir_format.html

ちょっと補足
キャリア:キャリアは使用する赤外線LEDで決まってしまいます。秋月で買える赤外線LED( http://akizukidenshi.com/catalog/g/gI-12612/ )は大概波長940μmです。要らないリモコンを分解して取り出しても構いません。半減角:15°(狭角)~60°(広角)や、LEDに流す電流で使い易さが変わってくるでしょう。
サブキャリア周波数:キャリアをパルス状にして出力する事で、ちまたに溢れている赤外線と、リモコンの赤外線を区別していますが、そのパルスの周波数です。一般的に38kHz前後です。

つまりリモコンコードのビット毎の0または1に対応するパターンに従って赤外線LEDをON/OFFさせれば良いことが判ります。この時ArduinoであればサブキャリアはanalogWrite、パルスの時間はdelayMicrosecondsを使えば実現できます。
/*************************************************************************/
/* NECタイプのデータ送信                                                 */
/*************************************************************************/
void iRRemocon::txNEC(const unsigned char data[], unsigned int sz)
{
  #define T_VALUE  555UL  // 562UL
  #define PPM_ON   128  //170
  #define PPM_OFF  255

  analogWriteRange( 255 );
  analogWriteFreq( 38 * 1000UL );

  analogWrite(_IR_CONT,0);
 //frame ppm on
  analogWrite(_IR_CONT,PPM_ON);
  delayMicroseconds(T_VALUE * 16);  //16T
  //frame ppm off
  analogWrite(_IR_CONT,PPM_OFF);
  delayMicroseconds(T_VALUE * 8);  //8T

  for(int i = 0; i < sz; i++)
  {
    byte temp = data[i];
    for(int j = 0; j < 8; j++)
    {
      if(temp & 0x01)
      {
        //frame ppm on
        analogWrite(_IR_CONT,PPM_ON);
        delayMicroseconds(T_VALUE * 1);  //1T
        //frame ppm off
        analogWrite(_IR_CONT,PPM_OFF);
        delayMicroseconds(T_VALUE * 3);  //3T
      }
      else
      {
        //frame ppm on
        analogWrite(_IR_CONT,PPM_ON);
        delayMicroseconds(T_VALUE * 1);  //1T
        //frame ppm off
        analogWrite(_IR_CONT,PPM_OFF);
        delayMicroseconds(T_VALUE * 1);  //1T
      }
      temp >>= 1;
    }
  }

  //stop bit
  //frame ppm on
  analogWrite(_IR_CONT,PPM_ON);
  delayMicroseconds(T_VALUE * 1);  //1T
  //frame ppm off
  analogWrite(IR_CONT,0);
}

ですがまぁ、折角エアコン制御を実現しているので、そこで使われている赤外線出力のClassを利用する事とします。

Classの使い方を知るために、三菱のエアコンのソースコードを見てみます。三菱のエアコンはAEHAフォーマットです。
MitsubishiHeatpumpIR.cpp
void MitsubishiHeatpumpIR::sendMitsubishi(IRSender& IR, uint8_t powerMode, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingV, uint8_t swingH)
{
  長いので抜粋
  // 40 kHz PWM frequency
  IR.setFrequency(38);

  // The Mitsubishi data is repeated twice
  for (int j=0; j<2; j++) {
    // Header
    IR.mark(MITSUBISHI_AIRCON1_HDR_MARK);
    IR.space(MITSUBISHI_AIRCON1_HDR_SPACE);

    // Data
    for (unsigned int i=0; i<sizeof(MitsubishiTemplate); i++) {
      IR.sendIRbyte(MitsubishiTemplate[i], MITSUBISHI_AIRCON1_BIT_MARK, MITSUBISHI_AIRCON1_ZERO_SPACE, MITSUBISHI_AIRCON1_ONE_SPACE);
    }

    // Pause between the first and the second data burst
    // Also modify one byte for the second burst on MSY model. This does not affect the checksum of the second burst
    if (j == 0) {
      IR.mark(MITSUBISHI_AIRCON1_BIT_MARK);
      IR.space(MITSUBISHI_AIRCON1_MSG_SPACE);

      if (_mitsubishiModel == MITSUBISHI_MSY) {
        MitsubishiTemplate[14] = 0x24;
      }
    }
  }

  // End mark
  IR.mark(MITSUBISHI_AIRCON1_BIT_MARK);
  IR.space(0);
}

MitsubishiHeatpumpIR.h
/*
    Mitsubishi MSZ FD-25 heatpump control (remote control P/N KM09D 0052376)
*/
#ifndef MitsubishiHeatpumpIR_h
#define MitsubishiHeatpumpIR_h

#include "HeatpumpIR.h"

  長いので抜粋

// Mitsubishi MSZ FD-25 timing constants (remote control P/N KM09D 0052376)
#define MITSUBISHI_AIRCON1_HDR_MARK   3500
#define MITSUBISHI_AIRCON1_HDR_SPACE  1700
#define MITSUBISHI_AIRCON1_BIT_MARK   430
#define MITSUBISHI_AIRCON1_ONE_SPACE  1250
#define MITSUBISHI_AIRCON1_ZERO_SPACE 390
#define MITSUBISHI_AIRCON1_MSG_SPACE  17500

class MitsubishiHeatpumpIR : public HeatpumpIR
{
  protected: // Cannot create generic Mitsubishi heatpump instances
    MitsubishiHeatpumpIR();
    uint8_t _mitsubishiModel;  // Tells whether this is FD or EF (or other supported model...)

  public:
    void send(IRSender& IR, uint8_t powerModeCmd, uint8_t operatingModeCmd, uint8_t fanSpeedCmd, uint8_t temperatureCmd, uint8_t swingVCmd, uint8_t swingHCmd);

  private:
    void sendMitsubishi(IRSender& IR, uint8_t powerMode, uint8_t operatingMode, uint8_t fanSpeed, uint8_t temperature, uint8_t swingVCmd, uint8_t swingHCmd);
};

#endif

定義のMITSUBISHI_AIRCON1_HDR_MARKは赤外線フレームのLeader部の8Tに、MITSUBISHI_AIRCON1_HDR_SPACEはLeader部の4Tに相当します。
MITSUBISHI_AIRCON1_BIT_MARKはパルスを出している時間、MITSUBISHI_AIRCON1_ONE_SPACEはビット1のパルスの無い時間、MITSUBISHI_AIRCON1_ZERO_SPACEはビット0のパルスの無い時間に相当します。
ChaNさんの解説と微妙に時間が合わないのは、調整が入っているのかもしれません。

上記を参考にオーデリックのライブラリを作成してみました。現時点ではZIPインストールをせず、プロジェクトディレクトリに保存しているので、includeパスに注意が必要です。
odelic.h
/****************************************************************************/
/* お家のオーデリック製LED照明をなんとかするヘッダー                        */
/*                         Copyright (C) 2014 hamayan All Rights Reserved.  */
/****************************************************************************/
#ifndef odelic_h
#define odelic_h

#include  <Arduino.h>
#include  <HeatpumpIR.h>

extern "C" {
//  #include  <mul_tsk.h>
}

/****************************************************************************/
/* なにかの定義                                                             */
/****************************************************************************/
#define  NEC_T_PERIOD  562UL  // 562us
#define  NEC_REPEAT_PERIOD  120UL  // 108ms
#define  ODELIC_CHANNEL_1  0
#define  ODELIC_CHANNEL_2  1
#define  ODELIC_CHANNEL_3  2

typedef struct  /*オーデリックのフォーマットはNECタイプ*/
{
  unsigned char customerCode[2];
  unsigned char data[2];
} ODELIC_CEILING_LIGHT_CODE;

class odelic : public HeatpumpIR
{
private:
  uint8_t channel;
  uint32_t startTim;
  void send( IRSender& IR, uint8_t *code );

public:
  odelic( uint8_t ch = 0 );
  void onOff( IRSender& IR, bool onoff );
  void coldLight( IRSender& IR, int value );
  void warmLight( IRSender& IR, int value );
  char type( void );
};


#endif  /*odelic_h*/

/****************************************************************************/
/*                         Copyright (C) 2014 hamayan All Rights Reserved.  */
/****************************************************************************/

odelic.cpp
/*************************************************************************/
/* お家のオーデリック製LED照明をなんとかするソース                             */
/*                                designed by hamayan since 2015/05/28  */
/*************************************************************************/
#include  "odelic.h"

/*************************************************************************/
/* 大域変数宣言                                                          */
/*************************************************************************/
/*調光を行う時は連続して2つのコードを送信する。つまり寒色と暖色の両方のLEDを制御している*/
/*また、輝度毎にコードが異なる。それ以外に3byte目と4byte目は反転の関係となる*/
static const unsigned char on[] = { 0x84, 0x51, 0x9a ,0x65 };  /*ch2: 0x84, 0x51, 0x9b,0x64*/
static const unsigned char off[] = { 0x84, 0x51, 0x08 ,0xf7 };  /*ch2: 0x84, 0x51, 0x09,0xf6*/
static const unsigned char cold[][4] =
{
  { 0x84, 0x51, 0x6c ,0x93 },  /*10% ch2: 0x84, 0x51, 0x6d,0x92*/
  { 0x84, 0x51, 0x6e ,0x91 },  /*20% ch2: 0x84, 0x51, 0x6f,0x90*/
  { 0x84, 0x51, 0x70 ,0x8f },  /*30% ch2: 0x84, 0x51, 0x71,0x8e*/
  { 0x84, 0x51, 0x72 ,0x8d },  /*40% ch2: 0x84, 0x51, 0x73,0x8c*/
  { 0x84, 0x51, 0x74 ,0x8b },  /*50% ch2: 0x84, 0x51, 0x75,0x8a*/
  { 0x84, 0x51, 0x76 ,0x89 },  /*60% ch2: 0x84, 0x51, 0x77,0x88*/
  { 0x84, 0x51, 0x78 ,0x87 },  /*70% ch2: 0x84, 0x51, 0x79,0x86*/
  { 0x84, 0x51, 0x7a ,0x85 },  /*80% ch2: 0x84, 0x51, 0x7b,0x84*/
  { 0x84, 0x51, 0x7c ,0x83 },  /*90% ch2: 0x84, 0x51, 0x7d,0x82*/
  { 0x84, 0x51, 0x7e ,0x81 },  /*100% ch2: 0x84, 0x51, 0x7f,0x80*/
};
static const unsigned char warm[][4] =
{
  { 0x84, 0x51, 0x58 ,0xa7 },  /*10% ch2: 0x84, 0x51, 0x59,0xa6*/
  { 0x84, 0x51, 0x5a ,0xa5 },  /*20% ch2: 0x84, 0x51, 0x5b,0xa4*/
  { 0x84, 0x51, 0x5c ,0xa3 },  /*30% ch2: 0x84, 0x51, 0x5d,0xa2*/
  { 0x84, 0x51, 0x5e ,0xa1 },  /*40% ch2: 0x84, 0x51, 0x5f,0xa0*/
  { 0x84, 0x51, 0x60 ,0x9f },  /*50% ch2: 0x84, 0x51, 0x61,0x9e*/
  { 0x84, 0x51, 0x62 ,0x9d },  /*60% ch2: 0x84, 0x51, 0x63,0x9c*/
  { 0x84, 0x51, 0x64 ,0x9b },  /*70% ch2: 0x84, 0x51, 0x65,0x9a*/
  { 0x84, 0x51, 0x66 ,0x99 },  /*80% ch2: 0x84, 0x51, 0x67,0x98*/
  { 0x84, 0x51, 0x68 ,0x97 },  /*90% ch2: 0x84, 0x51, 0x69,0x96*/
  { 0x84, 0x51, 0x6a ,0x95 },  /*100% ch2: 0x84, 0x51, 0x6b,0x94*/
};

/*************************************************************************/
/* プロトタイプ宣言                                                      */
/*************************************************************************/

/*************************************************************************/
/* インスタンス                                                          */
/*************************************************************************/

odelic::odelic( uint8_t ch )
{
  channel = ch;
  startTim = millis();
}


/*************************************************************************/
/* send                                                                  */
/*************************************************************************/
void odelic::send( IRSender& IR, uint8_t *code )
{
  uint8_t tempUC[4];
  memcpy( tempUC, code, sizeof(tempUC) );
  tempUC[2] = tempUC[2] + channel;
  tempUC[3] = tempUC[3] - channel;

  // 40 kHz PWM frequency
  IR.setFrequency( 38 );

  //リピートでメソッドが呼ばれた時、強制的に間隔を開ける
  while( (millis() - startTim) < NEC_REPEAT_PERIOD ) delay( 10 );
  // start time
  startTim = millis();

  // Header
  IR.mark( 16 * NEC_T_PERIOD );  //frame ppm on
  IR.space( 8 * NEC_T_PERIOD );  //frame ppm off

  // Data
  for( unsigned int i = 0; i < sizeof( tempUC ); i++ )
  {
    IR.sendIRbyte( tempUC[i], NEC_T_PERIOD, NEC_T_PERIOD, 3 * NEC_T_PERIOD );  // data,562us,562us,1686us
    // pulse on period, pulse off period at ZERO, pulse off period at 1
  }

  // End mark
  IR.mark( NEC_T_PERIOD );
  IR.space( 0 );
}

/*************************************************************************/
/* 引数valueは2以上で100以下の数字                                       */
/*************************************************************************/
void odelic::coldLight( IRSender& IR, int value )
{
  if( value >= 2 && value <= 100 )
  {
    value--;
    value /= 10;
    send( IR, (uint8_t *)cold[ value ] );
  }
}


/*************************************************************************/
/* 引数valueは2以上で100以下の数字                                       */
/*************************************************************************/
void odelic::warmLight( IRSender& IR, int value )
{
  if( value >= 2 && value <= 100 )
  {
    value--;
    value /= 10;
    send( IR, (uint8_t *)warm[ value ] );
  }
}


/*************************************************************************/
/* 引数valueは0または1またはそれ以上で100以下の数字                      */
/*************************************************************************/
void odelic::onOff( IRSender& IR, bool onoff )
{
  if( onoff ) send( IR, (uint8_t *)on );
  else send( IR, (uint8_t *)off );
}


/*************************************************************************/
/* リモコンフォーマットを答える                                          */
/*************************************************************************/
char odelic::type( void )
{
  return 'N';  /*NECのN!*/
}


/*************************************************************************/
/* end of file                                                          */
/*                                designed by hamayan since 2015/05/28  */
/*************************************************************************/

スケッチ側はこんなんなります。
#include  "odelic.h"

#define   IR_CONT    12  // Ir control

IRSenderBitBang irSender( IR_CONT );
odelic *room1 = new odelic( ODELIC_CHANNEL_1 );

/*************************************************************************/
/*  blynk virtual pin 2 function.                                        */
/*************************************************************************/
BLYNK_WRITE( V2 )
{ 
  int value = param.asInt();
  if( value )
  {
    room1->onOff( irSender, true );
    Blynk.virtualWrite( V4, 100 );
    Blynk.virtualWrite( V5, 100 );
  }
  else
  {
    room1->onOff( irSender, false );
    Blynk.virtualWrite( V4, 0 );
    Blynk.virtualWrite( V5, 0 );
  }
}

/*************************************************************************/
/*  blynk virtual pin 3 function.                                        */
/*************************************************************************/
BLYNK_WRITE( V3 )
{ 
  room1->coldLight( irSender, 70 );
  room1->warmLight( irSender, 70 );
  Blynk.virtualWrite( V4, 70 );
  Blynk.virtualWrite( V5, 70 );
}

/*************************************************************************/
/*  blynk virtual pin 4 function.                                        */
/*************************************************************************/
BLYNK_WRITE( V4 )
{ 
  int value = param.asInt();
  room1->warmLight( irSender, value );
}

/*************************************************************************/
/*  blynk virtual pin 5 function.                                        */
/*************************************************************************/
BLYNK_WRITE( V5 )
{ 
  int value = param.asInt();
  room1->coldLight( irSender, value );
}

オーデリックの天井照明は、隣り合う部屋でリモコンを使った時に混信しないように、3チャネルまで設定できます。

ESP-WROOM-02開発ボード

ESP-WROOM-02開発ボード

  • 出版社/メーカー: スイッチサイエンス(Switch Science)
  • メディア: おもちゃ&ホビー






nice!(0)  コメント(0) 

Arduinoライブラリでエアコン(HEAT PUMP)を制御 家庭でできるIoT! 2パルス目 [ESP32]

2019-12-06 16.11.56.png誰も期待していないでしょうけれど、Blynkで動かす。

いろいろな都合でエアコンは三菱に変更している
/*
  ダイキン,,,もとい三菱のエアコンの制御サンプル
  以下のライブラリを利用する。
  Arduinoのライブラリマネージャーからもインストールできる。
  https://github.com/ToniA/arduino-heatpumpir
*/
//#include  <DaikinHeatpumpIR.h>
#include  <MitsubishiHeatpumpIR.h>
#include  <ESP8266WiFi.h>
#include  <BlynkSimpleEsp8266.h>  /* Blynk library */
#include  "authentication.h"  /* 認証用ファイル */

/*************************************************************************/
/* defines                                                               */
/*************************************************************************/
#define   IR_CONT    12  // Ir control

/*************************************************************************/
/* instances                                                             */
/*************************************************************************/
IRSenderBitBang irSender( IR_CONT );  /* 赤外線送信用インスタンスの生成。引数は赤外線LEDが接続されているピン番号 */
//HeatpumpIR *airCon = new DaikinHeatpumpIR();  /* ダイキンのエアコンのインスタンスの生成 */
HeatpumpIR *airCon = new MitsubishiFDHeatpumpIR();  /* 三菱のエアコンのインスタンス */
IPAddress myIP; // self IP address
// Attach virtual serial terminal to Virtual Pin V1
WidgetTerminal terminal(V1);

/*************************************************************************/
/* global variables                                                      */
/*************************************************************************/
/*wifi AP informations*/
const struct ACCESS_POINT_LIST
{
  const char *ssid;
  const char *pass;
} ap[] =
{
  { SSID_STATION_HOME, PASSWORD_STATION_HOME },
  { SSID_STATION, PASSWORD_STATION },
};
char blynkAuth[] = BLYNK_AUTH;

/***************************************************************************/
/* associate to  wifi AP                                                   */
/***************************************************************************/
int associateToAP( const char *id, const char *ps )
{
  WiFi.begin( id, ps );
  int i = 0;
  while( WiFi.status() != WL_CONNECTED )  /*だいたい8秒から10秒くらい接続に掛かっている。*/
  {
    delay( 100 );
    Serial.print( "." );
    if(++i >= (15000 / 100))  /*15秒で諦める*/
    {
      return (-1);  //end of routine
      ESP.reset();
      //ESP.deepSleep(1 * 1000 * 1000UL, WAKE_RF_DEFAULT );  //old code was WAKE_RF_DISABLED 
      delay( 100 );
    }
  }
  return i;
}

/*************************************************************************/
/* setup                                                                 */
/*************************************************************************/
void setup()
{
  Serial.begin( 115200 );
  Serial.println();
  pinMode( IR_CONT, OUTPUT );
  digitalWrite( IR_CONT, LOW );

  /*アクセスポイントへ接続*/
  Serial.println( "connect to AP." );
  if( associateToAP( ap[ 0 ].ssid, ap[ 0 ].pass ) < 0 )
  {
    /*接続に失敗した時、リストから検索して接続してみる*/
    int i;
    for( i = 0; i < sizeof(ap) / sizeof(ap[0]); i++ )
    {
      int ret = associateToAP( ap[i].ssid, ap[i].pass );
      if( ret >= 0 )
      {
        Serial.print( "\r\n AP connect time = " );
        Serial.print( ret * 100, DEC );
        Serial.println( "ms" );
        break;
      }
    }
    if( i == sizeof(ap) / sizeof(ap[0]) )  /*接続に失敗した場合*/
    {
      ESP.reset();
      //ESP.deepSleep( 1 * 1000 * 1000UL, WAKE_RF_DEFAULT );  //old code was WAKE_RF_DISABLED
      delay(100);
    }
  }
  myIP = WiFi.localIP();
  Serial.print( "\r\nIP number assigned by DHCP is " );
  Serial.println( myIP );

  /* initialize blynk. */
//  Blynk.begin( blynkAuth, ssid, pass );
  Blynk.config( blynkAuth );
}

/*************************************************************************/
/* loop                                                                  */
/*************************************************************************/
void loop()
{
  Blynk.run();  /* executing blynk. */
}

/*************************************************************************/
/*  blynk virtual pin 0 function.                                        */
/*************************************************************************/
BLYNK_WRITE( V0 )
{ 
  int value = param.asInt();
  if( value )
  {
    /* ir instance,power,mode,fan,temperature,swing vertical,swing horizontal */
    airCon->send( irSender, POWER_ON, MODE_AUTO, FAN_AUTO, 24, VDIR_DOWN, HDIR_AUTO );  /* power on */
    terminal.println( "power on." );
  }
  else
  {
    /* ir instance,power,mode,fan,temperature,swing vertical,swing horizontal */
    airCon->send( irSender, POWER_OFF, MODE_AUTO, FAN_AUTO, 24, VDIR_DOWN, HDIR_AUTO );  /* power off */
    terminal.println( "power off." );
  }
  terminal.flush();
}


ChaNさんのリモコンコードの解説ページ、、、httpsではない!
http://elm-chan.org/docs/ir_format.html


ESP-WROOM-02開発ボード

ESP-WROOM-02開発ボード

  • 出版社/メーカー: スイッチサイエンス(Switch Science)
  • メディア: おもちゃ&ホビー









nice!(0)  コメント(0) 

Arduinoライブラリでエアコン(HEAT PUMP)を制御 家庭でできるIoT! 1パルス目 [ESP32]

2019-12-06 11.50.42.jpg
寒くなってきたので、エアコンを制御してみましょう!と言う話です。
カテゴリはESP32ですが、使っているのはESP8266です。あまり変わりませんが!


さて、とっくに寒くなってるじゃんね~!って感じですが、昔からやっているネタの再発掘。

エアコン、業務用とかではなければ赤外線リモコンが付属していると思います。
このエアコンの赤外線制御は結構面倒くさい感じでして、
1.メーカーがリモコンコードの仕様を出さない
2.コード長が100bitを超える
など自分の家のエアコンのコードを解析するのも一苦労です。

んが、世の中には地道にコード解析、ライブライを提供してくれる人もおるんですね!
ここで公開されているライブラリを利用しています。
https://github.com/ToniA/arduino-heatpumpir

HEATPUMPで検索すれば、Arduino IDEからライブラリ管理でインストールできます。
また、ライブラリ管理からIRremoteESP8266もインストールしておきます。多分、、、
ソースコードが有るので、Arduino以外の環境への移植も可能な気がします。

ダイキンのエアコンの電源入り切りサンプルです。
ESP8266の12ピンに赤外線LEDを接続しました。12ピン側がアノード、カソード側は100Ωの抵抗を通してGNDに接続しています。
赤外線が出ているかどうかは見れば、、、いえ目で見ても判らないので、スマフォのカメラを通してみればなんとなく判りますね。
/*
  ダイキンのエアコンの制御サンプル
  以下のライブラリを利用する。
  Arduinoのライブラリマネージャーからもインストールできる。
  https://github.com/ToniA/arduino-heatpumpir
*/
#include <DaikinHeatpumpIR.h>

/*************************************************************************/
/* defines                                                               */
/*************************************************************************/
#define   IR_CONT    12  // Ir control

/*************************************************************************/
/* instances                                                             */
/*************************************************************************/
IRSenderBitBang irSender( IR_CONT );  /* 赤外線送信用インスタンスの生成。引数は赤外線LEDが接続されているピン番号 */
HeatpumpIR *airCon = new DaikinHeatpumpIR();  /* ダイキンのエアコンのインスタンスの生成 */

/*************************************************************************/
/* setup                                                                 */
/*************************************************************************/
void setup()
{
  Serial.begin( 115200 );
  pinMode( IR_CONT, OUTPUT );
  digitalWrite( IR_CONT, LOW );

  while ( 1 )
  {
    /* ir instance,power,mode,fan,temperature,swing vertical,swing horizontal */
    airCon->send( irSender, POWER_ON, MODE_HEAT, FAN_3, 24, VDIR_DOWN, HDIR_AUTO );  /* power on */
    delay( 30 * 1000UL );
    airCon->send( irSender, POWER_OFF, MODE_HEAT, FAN_3, 24, VDIR_DOWN, HDIR_AUTO );  /* power off */
break;
    delay( 10 * 1000UL );
  }
}

/*************************************************************************/
/* loop                                                                  */
/*************************************************************************/
void loop()
{
}

ライブラリを組み込んでみればエアコンの制御は非常に簡単だと思います。つくづくライブラリ提供者に感謝です。
自分の家のエアコンにどのリモコンコードを適用すればイイのか?は、ライブラリの作例にsimpleが有り、これを実行すれば判ります。

ライブラリの使い方が判ってしまえば、これをBlynkで制御!とか余裕ですね!

ESP-WROOM-02開発ボード

ESP-WROOM-02開発ボード

  • 出版社/メーカー: スイッチサイエンス(Switch Science)
  • メディア: おもちゃ&ホビー



nice!(0)  コメント(0) 

EXCEL VBA備忘録 ACCESS連携 [EXCEL]

リファレンスはまずはここを見る?
https://docs.microsoft.com/ja-jp/office/vba/api/overview/excel

何と言いますか今更ですが、エクセルVBAをやってみたんですよん。

2019-10-29.png意外と使える!と言うかデータ収集してエクセルにダイレクトにシートやセルに代入できるのは、やはり魅力的なので。

プログラムの観点からエクセルを理解するのは、おもしろいかもしれない!


ACCESS連携
!ACCESSのテーブルを展開する。※クエリでも同じ
  ' データベースを開く
  Dim DBE As Object, DB As Object, buhinRS As Object
  Set DBE = CreateObject("DAO.DBEngine.120")
  Set DB = DBE.OpenDatabase(filePath)  ' filePathはACCESSのデータベースへのファイルパス
  Set buhinRS = DB.OpenRecordset("部品")  ' 部品テーブルを引っ張る
  Dim recordCount As Long
  recordCount = buhinRS.recordCount - 2  ' レコードの数を取得するが、なぜか2多い
  
  '書式再設定 テーブルのフィールドタイプの合わせる
  Worksheets("BUHIN").Range("A2", Worksheets("BUHIN").Cells(recordCount+2, 1)).NumberFormatLocal = "0"
  Worksheets("BUHIN").Range("B2", Worksheets("BUHIN").Cells(recordCount+2, 2)).NumberFormatLocal = "yyyy/mm/dd"
  Worksheets("BUHIN").Range("C2", Worksheets("BUHIN").Cells(recordCount+2, 3)).NumberFormatLocal = "0"
  Worksheets("BUHIN").Range("D2", Worksheets("BUHIN").Cells(recordCount+2, 4)).NumberFormatLocal = "@"
  Worksheets("BUHIN").Range("E2", Worksheets("BUHIN").Cells(recordCount+2, 5)).NumberFormatLocal = "0"
  Worksheets("BUHIN").Range("F2", Worksheets("BUHIN").Cells(recordCount+2, 6)).NumberFormatLocal = "@"
  
  ' フィールド個別にセルに代入していく
  Dim row As Long
  row = 2
  Do Until buhinRS.EOF
    Worksheets("BUHIN").Cells(row, 1).Value = buhinRS(0)
    Worksheets("BUHIN").Cells(row, 2).Value = buhinRS(1)
    Worksheets("BUHIN").Cells(row, 3).Value = buhinRS(2)
    Worksheets("BUHIN").Cells(row, 4).Value = buhinRS(3)
    Worksheets("BUHIN").Cells(row, 5).Value = buhinRS(4)
    Worksheets("BUHIN").Cells(row, 6).Value = buhinRS(5)
    
    row = row + 1
    buhinRS.MoveNext
  Loop

  buhinRS.Close
  Set buhinRS = Nothing
  DB.Close
  Set DB = Nothing

!データの並び替え
データベースとは関係ないが、どちらかと言えば人間が見やすくする為にレコードの並び替えをする。
  ' データソート
  Worksheets("BUHIN").Range("A2", Worksheets("BUHIN").Cells(row, 6)) _
    .Sort Key1:=Worksheets("BUHIN").Range("C1"), order1:=xlAscending

!検索
並び替えの次は検索でしょう!Rangeで範囲指定しないと、トンでもな結果になる。すごく悩んだ。
codeが検索したい文字
  Dim Obj As Object
  Set Obj = Worksheets(sheet).Range("C2", Worksheets(sheet).Cells(downCell.row, 2)).Find( _
        After:=Worksheets(sheet).Range("C2"), _
        What:=code, _
        LookIn:=xlValues, _
        LookAt:=xlWhole, _
        SearchOrder:=xlByColumns)
  
  If Obj Is Nothing Then
  ' 見つからなかった時の処理
  Else
  ' 見つかった時の処理
  End If


以下、順次追加

今読んでいる本

Excel VBAの教科書 (Informatics & IDEA)

Excel VBAの教科書 (Informatics & IDEA)

  • 作者: 古川 順平
  • 出版社/メーカー: SBクリエイティブ
  • 発売日: 2018/07/21
  • メディア: 単行本



気になっている本

パーフェクトExcel VBA (PERFECT SERIES)

パーフェクトExcel VBA (PERFECT SERIES)

  • 作者: 高橋 宣成
  • 出版社/メーカー: 技術評論社
  • 発売日: 2019/11/25
  • メディア: 単行本(ソフトカバー)



nice!(0)  コメント(0) 

EXCEL VBA備忘録 制御しよう! [EXCEL]

リファレンス先として、まずはここ?
https://docs.microsoft.com/ja-jp/office/vba/api/overview/excel

何と言いますか今更ですが、エクセルVBAをやってみたんですよん。

2019-10-29.png意外と使える!と言うかデータ収集してエクセルにダイレクトにシートやセルに代入できるのは、やはり魅力的なので。

プログラムの観点からエクセルを理解するのは、おもしろいかもしれない!


制御関連
!時間待ち
Public Declare PtrSafe Sub Sleep Lib "kernel32.dll" (ByVal dwMilliseconds As Long) 
    Sleep (100)  ' 100ms待ち
    DoEvents  ' エクセルに制御を返す

!時間の取得
  Dim startTimer As Double
  startTime = Timer

!シリアル通信タイムアウト設定
'SetCommTimeouts
Private Type COMMTIMEOUTS
    ReadIntervalTimeout As Long         ' 文字間の受信の待ち時間
    ReadTotalTimeoutMultiplier As Long  ' 受信文字数
    ReadTotalTimeoutConstant As Long    ' 固定受信待ち時間
    WriteTotalTimeoutMultiplier As Long ' 送信文字数
    WriteTotalTimeoutConstant As Long   ' 固定送信待ち時間
End Type

Private Declare PtrSafe Sub SetCommTimeouts Lib "kernel32" _
   (ByVal hfile As Long, _
          lpCommTimeouts As COMMTIMEOUTS)

  'configure com port timeout.
  ct.ReadIntervalTimeout = 0  ' 1000
  ct.ReadTotalTimeoutMultiplier = 1  ' 1
  ct.ReadTotalTimeoutConstant = 10  ' 500
  ct.WriteTotalTimeoutMultiplier = 1
  ct.WriteTotalTimeoutConstant = 500
  SetCommTimeouts hfile, ct

※1byteずつ文字を取得する様な場合は、このタイムアウトを適切に設定しないといけない。

!デバック中にCOM PORTを掴んだままプログラムが終了してしまった!
何も対策をしていないと一旦ワークシートを閉じないとCOM PORTの再利用ができない。
Subプロシージャ等でイベントドリブンでプログラムが動いているなら、COM PORTをオープンした時のハンドルをワークシートの一部とか大域変数にコピーして、次のCOM PORTオープンで失敗したらそのバックアップしたハンドルでクローズすると、わりと上手く行く。
以下はワークシートにバックアップした例。
  ' open com port.
  Dim hDev0 As Long
  hDev0 = comPortOpen(comPort, baud)
  If hDev0 = -1 Then
    MsgBox "Can't Open COM" & comPort
    If ActiveSheet.Range(COM_PORT_HANDLE_INPUT).Value <> "" Then
      hDev0 = ActiveSheet.Range(COM_PORT_HANDLE_INPUT).Value
      CloseHandle hDev0
    End If
    GoTo ENDOfButton1Click
  End If
  ActiveSheet.Range(COM_PORT_HANDLE_INPUT).Value = hDev0

!ワークシート上のボタンをいじる
  Worksheets("sheet1").Buttons(1).Caption = "受信中"  ' キャプションの変更
'  Worksheets("sheet1").Buttons("ボタン1_Click").Caption = "受信中"    ' 上手く行かない
'  Worksheets("sheet1").Buttons(1).Visible = False  ' シート上から消える

!データ受信に専念する
データ受信中もワークシート上のグラフとかはセルの内容に応じて再描画されてしまい、全体の処理が遅くなってしまうので、その再描画の抑制をしてみる。
' 再計算を行うの?行わないの?
' no return value.
Function verbosedSheet(ByVal yesNo As Boolean)
  If yesNo Then
    Application.Calculation = xlCalculationAutomatic
'    Application.ScreenUpdating = True  ' これを行うと本当に画面の変化が無くなる
    Application.EnableEvents = True
  Else
    Application.Calculation = xlCalculationManual
'    Application.ScreenUpdating = False  ' これを行うと本当に画面の変化が無くなる
    Application.EnableEvents = False
  End If
End Function

!一行分のCSVデータをセルに代入
' write csv data to cells.
' no return value.
Function writeCSVDataToCell(ByVal y As Integer, ByVal x As Integer, ByVal line As String)
  Dim arr() As String
  arr = Split(line, ",")
  Dim argc As Integer
  argc = UBound(arr)
  ReDim Preserve arr(argc)
  
  Dim i As Integer
  For i = 1 To argc
    If arr(i - 1) <> "" Then
      ActiveSheet.Cells(y, x).Value = arr(i - 1)
      x = x + 1
    End If
  Next
End Function

!ワークシートを開いたり閉じたりした時になにかをさせる。
Private Sub Workbook_Open()  ' 起動時実行ハンドラ
Private Sub Workbook_BeforeClose(Cancel As Boolean)  '終了時実行ハンドラ

!ワークシートの縦横のサイズを取得
  Dim rowSize As Long
  rowSize = Rows.Count  ' ワークシートの横方向の大きさを返すプロパティ
  Dim colSize As Long
  colSize = Columns.Count  ' ワークシートの縦方向の大きさを返すプロパティ
  MsgBox "row size = " & rowsize
  MsgBox "col size = " & colsize

!指定したセルから、各方向の値が含まれる最後のセルを取得する。
  '  xlDown:下方向 xlToRight:右方向 xlToLeft:左方向 xlUp:上方向
  Dim range1 As Range

  Set range1 = ActiveSheet.Range("C3").End(xlDown)
  Set range1 = ActiveSheet.Cells(3, 3).End(xlDown) ' または
  range1.Value = "下端のセル"

  Set range1 = ActiveSheet.Range("C3").End(xlToRight)
  Set range1 = ActiveSheet.Cells(3, 3).End(xlToRight)  ' または
  range1.Value = "右端のセル"

※上記のセルのrowとcolumnを得るには、それぞれのrangeのcolumnプロパティとrowプロパティを参照する。range1.Columnとかrange1.rowとか。以下の様に!
Sub recieveAreaClear()
  Dim downCell As Range, rightCell As Range
  Set downCell = ActiveSheet.Range("A3").End(xlDown)
  Set rightCell = ActiveSheet.Range("A3").End(xlToRight)
  
  Dim col As Long, row As Long
  col = rightCell.Column
  row = downCell.row

  ' MsgBox "col = " & col & " row = " & row
  If col <= 0 Or col > 100 Then
    col = ActiveSheet.Range("A3").Column
  End If
  If row <= 0 Or row > 1000 Then
    row = ActiveSheet.Range("A3").row
  End If
  Range("A3", Cells(row, col)).Clear
End Sub


!カラーコードをインデックスで操作。カラーコードは56有ると言う話。
Sub colorAsIndex()
  Dim cell As Range
  Dim row As Integer, col As Integer
  Dim code As Integer
  code = 1
  For row = 0 To 6  ' 行
    For col = 0 To 7  ' 列
      Set cell = Range("A1").Offset(RowOffset:=row, ColumnOffset:=col)
      cell.Interior.colorIndex = code
      code = code + 1
    Next col
  Next row
End Sub

!カラーコードをRGBで操作
Sub colorAsRGB()
  Dim cell As Range
  Dim row As Integer, col As Integer
  Dim red As Integer, green As Integer, blue As Integer
  red = 0
  green = 0
  blue = 0
  For row = 0 To 15  ' 行
    For col = 0 To 15  ' 列
      Set cell = Range("A1").Offset(RowOffset:=row, ColumnOffset:=col)
      cell.Interior.Color = RGB(red, green, blue)
      red = red + 1
      green = green + 1
      blue = blue + 1
    Next col
  Next row
End Sub

!セルの書式指定
RS232Cで送信するAPIはString型しか受け付けないので、セルの文字列を送信しようと言う時にString型ではない値が入っているとエラーとなる。元のエクエルのワークシートで書式指定してもイイのだろうけれど、、、
  ActiveSheet.Range(COM_PORT_INPUT).NumberFormatLocal = "0" ' 数値  "0_"では上手く行かない
  ActiveSheet.Range(TRANSMIT_LINE).NumberFormatLocal = "@" ' 文字列 "G/標準"とすると標準

!RS232Cで日本語が送れる?
エクセルのセルに入力された日本語はおそらくUTF8なのだろうけれど、これをRS232CのAPIの送信関数に与えると上手く行かない。何か方法は有るのだろうけれど、、、

!String型の中身を知りたい
上記にも関係するのだけれど、上手く行かない時に中身を調べる方法
bDataへの代入の直後にデバッカーで止めて変数の中身を確認
    Dim sndData As String
    sndData = ActiveSheet.Range(TRANSMIT_LINE).Value + Chr(CR) + Chr(LF)
  
    Dim bData() As Byte
    bData = sndData

    Dim sndSize As Long
    WriteFile hDev0, sndData, Len(sndData), sndSize, 0

!なんとなくマルチスレッドでRS232Cの受信をしてみたい、、、
とは言えVBA自体にマルチスレッドがサポートされていないらしく、検索すると結構複雑な手順でマルチスレッドっぽい方法を編み出している様だ。せめて受信くらいはなんとかならないのか?

イベントドリブンなVBAでシートに張り付けたボタンに以下のSubプロシージャを登録してみた。
肝はDoEventsと、受信タイムアウト関連のプロパティを設定して、何もデータが無ければすぐに帰ってくる様にしないと。だからと言ってスマートな方法とは思えない、、、
※connectionIsは別のCOMポートのOPEN/CLOSEを行っているボタンの処理の中で書き換える大域変数
' line read from serial.
' return value is recived data bytes.
Function lineRead(ByVal hfile As Long, ByRef line As String) As Long
  Dim loops As Integer
  Dim length As Long
  Dim rcvByteSize As Long
  Dim bData() As Byte
  Dim bString As String
  
  line = ""
  loops = 0
  rcvByteSize = 0
  Do While loops < 100  ' loops is loop limitter.
    bString = String(1, vbNullChar)
    ReadFile hfile, bString, 1, length, ByVal 0  ' read one byte from serial.

    If length = 0 Then  ' if there is no data.
      If rcvByteSize = 0 Then
        lineRead = -1
        Exit Function
      End If
      delay 10
    Else
      loops = 0
      bData = bString
      ReDim Preserve bData(1)
    
      If bData(0) <> CR And bData(0) <> LF Then
        line = line + CStr(Chr(bData(0)))
        rcvByteSize = rcvByteSize + 1
      End If
      
      If bData(0) = LF Then
        Exit Do
      End If
    
    End If
    
    loops = loops + 1
  Loop
  
  If loops < 100 Then
    lineRead = rcvByteSize
  Else
    lineRead = -1
  End If

End Function

Sub dataReadingClick()
  Worksheets("sheet1").Buttons(1).Caption = "受信中"
  
  Dim rcvSize As Long
  Dim line As String
  
  Do While connectionIs = True
    rcvSize = lineRead(hDev0, line)
    
    If rcvSize > 0 Then
      ActiveSheet.Range(RECIEVE_LINE).Value = line
    End If
    
    DoEvents  ' 必ずこれを入れる事
  Loop
  Worksheets("sheet1").Buttons(1).Caption = "受信開始"
End Sub

!オブジェクトのプロパティを知りたい、、、
普通、RADツールであればボタンとかなんとかのプロパティやメソッドは一覧になってIDEから参照できたりするじゃないですか。でもワークシート上に貼り付けたボタンのプロパティをVBEで探しても見付ける事はできなかった。なぜ?そもそもCode Windowに行番号も表示できないし。
例えばボタンのキャプションを変更するのにこんな感じで指定している訳です。
  Worksheets("sheet1").Buttons( 1 ).Caption = "受信開始"

Buttonsの引数に数字を代入しているのです。ボタンの名前とかではなく、、、数字、、、
しょうがないのでWEBを検索してプロパティを調べる方法を見つけました。
Sub buttonListTest()
  Dim Button As Object
  For Each Button In ActiveSheet.Buttons
    Debug.Print "NUMBER =" & Button.Index & " Text = " & Button.Characters.Text & "; Actions = " & Button.OnAction; ""
  Next Button
End Sub

!シート上のボタンを全て削除
上記の応用編です。
Sub clearAllButtons()
  Dim Button As Object
  For Each Button In ActiveSheet.Buttons
    Button.Delete
  Next Button
End Sub

!ボタンを数字でスッキリと扱いたい
Const COM_PORT_OPEN_CLOSE = "E1"
Const COM_PORT_OPEN_CLOSE_WIDTH_HEIGHT = "E1:E2"
Public comPortOpenButtonIndex As Integer ' エクセルを開いたときに実行されるThisWorkbook code window上に、大域変数として番号を収納する変数を用意

起動時実行ハンドラの
Private Sub Workbook_Open()
の中でボタンを生成。この時、先の大域変数にIndexを保存しておく。
  Call clearAllButtons  ' 一旦、存在しているボタンを全て削除
  
  ' 接続ボタンの配置
  With ActiveSheet.Buttons.Add( _
    Range(COM_PORT_OPEN_CLOSE).Left, _
    Range(COM_PORT_OPEN_CLOSE).Top, _
    Range(COM_PORT_OPEN_CLOSE_WIDTH_HEIGHT).Width, _
    Range(COM_PORT_OPEN_CLOSE_WIDTH_HEIGHT).Height)
    .OnAction = "comPortOpenButtonClick"
    .Characters.Text = "接続"
    comPortOpenButtonIndex = .Index
  End With
  Debug.Print "comPortOpenButtonIndex = " & comPortOpenButtonIndex

標準モジュールの参照側は以下の様にIndexにアクセスする。
  ActiveSheet.Buttons(ThisWorkbook.comPortOpenButtonIndex).Caption = "接続中"


以降、順次追加

今読んでいる本

Excel VBAの教科書 (Informatics & IDEA)

Excel VBAの教科書 (Informatics & IDEA)

  • 作者: 古川 順平
  • 出版社/メーカー: SBクリエイティブ
  • 発売日: 2018/07/21
  • メディア: 単行本



気になっている本

パーフェクトExcel VBA (PERFECT SERIES)

パーフェクトExcel VBA (PERFECT SERIES)

  • 作者: 高橋 宣成
  • 出版社/メーカー: 技術評論社
  • 発売日: 2019/11/25
  • メディア: 単行本(ソフトカバー)



nice!(0)  コメント(0) 
前の10件 | -

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。