SSブログ

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) 

EXCEL VBA備忘録 [EXCEL]

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

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

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


勉強してみますか!

参考リンク
VB(Visual Basic)のメモさん
http://www.pursue.ne.jp/Document_doc/doc0009.htm

ExcelVBAプログラミング・メモさん
https://bluefish.orz.hm/sdoc/vba_memo.html

Excel VBA 入門さん
https://www.sejuku.net/blog/category/programing/excel-vba/excel-vba-primer

Excel VBA入門さん
https://www.officepro.jp/excelvba/

Excel VBAでRS-232C通信さん
https://qiita.com/pbjpkas/items/f81947ce38941356ebe4
※64bit OSでやるには、宣言文のDeclareの後ろにPtrSafeを付けなければならない。

RS232C シリアル通信さん
http://www.ys-labo.com/BCB/2007/070512%20RS232C%20zenpan.html
※SetupCommで送受信バッファサイズを変更できる。標準は幾つ?

RS232Cサンプル2(Open,Close,送信,受信の分離). - So-netさん
http://www007.upp.so-net.ne.jp/tmh_ogaw/soft/excel_sample/RS232C_121122_04_.bas
※上記同様

NonSoftさん COMポート(シリアル)で電文の送受信をするサンプル
http://nonsoft.la.coocan.jp/SoftSample/VC/SampleRs232c.html
※ライブラリ化されて物もある

使用できるCOMポートを取得するさん
https://blogs.yahoo.co.jp/sirius_cma1/2519308.html

VBA セルに表示形式を設定するさん
https://www.tipsfound.com/vba/07015

以降、順次追加


なんと言いますか、シリアルデータをESP32でGoogle Spread sheetに飛ばした方が、現代的かなぁ、、、
http://www.telomere0101.site/archives/post-1008.html

今読んでいる本

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) 

Socket Debuggerを使ってみる! ポート:12!POSTの実装に向けて [NETWORK]

SocketDebugger
こことか、 https://www.udom.co.jp/sdg/
こことか、 http://sdg.ex-group.jp/lua.html
をまず見てね!

webServer_007.png内部処理を文字列で行うのか?テーブルで行うのか?どっちが得なんだよ!って事でテーブルで処理する事とします。
なんかあれですね、ファイル書き込みにappendモードが無いので受信データを一旦全部テーブルに保存している訳です。ファイル受信試験で数十kbyteのファイルはイイですが、ちょっと大きな、と言っても4Mbyte程度のjpgですがね、メモリが足りなくなってスクリプトの実行エラーが起きますね。
でも同じJPGファイルの送信時には発生しないのは謎ですね。


webServer_008.pngあとあれですね、全体的に転送が重いので、細かく多量の処理をするのではなく、こうまとまった受信データをいっきに処理したい場合は、内部プログラム受信バッファサイズを大きくとってしまいましょう!と言う話でやんす。



ソース全文
---------------------------------------------
-- 接続完了通知
---------------------------------------------
function OnConnected()
  Logput(1,'OnConnected')

  recvHttpTable = {}  -- 受信データの一時格納用
  recievedContentTable = {}  -- HTTP文の内容格納用
  recievedUrlStr = ""  -- HTTP文のURL
  contentLength = 0  -- HTTP文の内容の取り込むべきサイズ
  operationCode = 0  -- 処理コード
 
 return 0
end
---------------------------------------------
-- 送信ボタン押下
---------------------------------------------
function OnSendPush()
    Logput(1,'OnSendPush')
    a = GetEditorData()
    SendData(a)
    return 0
end
---------------------------------------------
-- タイマー通知
---------------------------------------------
function OnTimer(id)
    Logput(1,'OnTimer')
    return 0
end
---------------------------------------------
-- 受信通知
-- 0x0D is CR,0x0A is LF
---------------------------------------------
function OnReceive(recv)
  Logput(1,'OnReceive')

  if operationCode == 0 then  -- 初期状態?
    recvHttpTable = TableCat( recvHttpTable, recv )  -- 一時格納用テーブルに受信データを保存
    if #recvHttpTable < 4 then return 0 end  -- 配列の長さを確認

    local head
    local body
    head,body = getHttpHeader( recvHttpTable )  -- 一時格納用テーブルからheaderとbodyに分ける

    if head ~= nil then  -- headerに内容が入っている時
      httpRequest( head, body )  -- HTTPリクエスト処理

      if operationCode == 200 then  -- 処理コード別の処理
        Disconnect()
      elseif operationCode == 400 then
        Disconnect()
      elseif operationCode == 404 then
        Disconnect()
      elseif operationCode == 405 then
        Disconnect()
      else
      end

    else  -- 受信はしたが、HTTP文の全てを受け取った訳ではない場合もある
      return 0
    end
  elseif operationCode == 201 then
    recievedContentTable = TableCat( recievedContentTable, recv )  -- HTTP文の内容格納用テーブルに受信データを保存

    if #recievedContentTable >= contentLength then  -- 全て内容を受信できた場合
      methodPost( recievedUrlStr, recievedContentTable )  -- method postの実行
      Disconnect()
    end
  end

  return 0
end
---------------------------------------------
-- 切断通知
---------------------------------------------
function OnDisConnected()
  Logput(1,'OnDisConnected')
  Disconnect()
  return 0
end

---------------------------------------------
-- http request
---------------------------------------------
function httpRequest( head, body )

  local header = CharFromTbl( head )  -- 引数headの内容を文字列に変換
  local headerLine = strSplit( header, "\r\n" )  -- 一行目を取得
  local line1 = strSplit( headerLine[1], " " )  -- 一行目をスペースで区切る

  local methodStr = line1[1]       -- 最初の単語
  local urlStr = line1[2]          -- 2番目の単語
  local httpVersionStr = line1[3]  -- 3番目の単語
  recievedUrlStr = urlStr  -- 大域変数にも保存

  if strCmp( methodStr, 'GET' ) == 0 then  -- GETの場合
    local fileData = {}
    local mimeType = ""
    fileData, mimeType = methodGet( urlStr )  -- 構文解析して要求されたファイルデータとmime typeを返す
    if fileData ~= nil and mimeType ~= "" then 
      index200( fileData, mimeType )  -- ファイル返信処理
      operationCode = 200  -- 処理コード
    else
      index404()  -- 該当ファイルが見つからない場合
      operationCode = 404  -- 処理コード
    end

  elseif strCmp( methodStr, 'PUT' ) == 0 then  -- PUTの場合
    index405()  -- 該当METHODが許されていない場合
    operationCode = 405  -- 処理コード

  elseif strCmp( methodStr, 'POST' ) == 0 then  -- POSTの場合
    contentLength = 0  -- HTTP文の内容の取り込むべきサイズを一旦0にする
    for i = 1, #headerLine do  -- headerの行数分ループ
      local compStr = "Content-Length:"  -- 探したいのはContent-Length
      if strNCmp( headerLine[i], compStr, #compStr ) == 0 then  -- 文字列の比較 
        local str = strSplit( headerLine[i], " " )  -- スペースで文字列を分割
        contentLength = tonumber( str[2] )  -- 2番目の単語を数値に変換
        break
      end
    end

    if contentLength > 0 then  -- ヘッダーからContent-Lengthを取得できたなら
      if #body > 0 then  -- 引き続きコンテンツが存在する?
        if #body >= contentLength then  -- 全てのコンテンツを受信済み?
          methodPost( urlStr, body )  -- method postの実行
          operationCode = 200  -- 処理コード
        else  -- 全てのデータの受信が完了していないとき
          recievedContentTable = TableCat( recievedContentTable, body )  -- 受信コンテンツ格納用テーブルに追加保存
          operationCode = 201  -- 処理コード
        end
      end
    else  -- 構文解析失敗?
      operationCode = 200  -- 処理コード
    end

  elseif strCmp( methodStr, 'DELETE' ) == 0 then  -- DELETEの場合
    index405()  -- 該当METHODが許されていない場合
    operationCode = 405  -- 処理コード

  else
    index400()  -- 不正なリクエスト
    operationCode = 400  -- 処理コード
  end
end


---------------------------------------------
-- method get
---------------------------------------------
function methodGet( urlStr )
  local file = {}
  local methodTbl = 
  {
    { url = "/",           path = "D:\\temp\\index.html", mime = "text/html" },
    { url = "/index.html", path = "D:\\temp\\index.html", mime = "text/html" },
    { url = "/index.htm",  path = "D:\\temp\\index.html", mime = "text/html" },
    { url = "/favicon.ico",  path = "D:\\temp\\favicon.ico", mime = "image/png" },
    { url = "/text.txt",  path = "D:\\temp\\text.txt", mime = "text/plain" },
    { url = "/photo1.jpg",  path = "D:\\temp\\photo1.jpg", mime = "image/jpeg" },
    { url = "/image1.png",  path = "D:\\temp\\image1.png", mime = "image/png" },
    { url = "/image2.bmp",  path = "D:\\temp\\image2.bmp", mime = "image/bmp" },
    { url = "/test.csv",  path = "D:\\temp\\test.csv", mime = "text/csv" },
--    { url = "/test.bin",  path = "D:\\temp\\test.bin", mime = "application/octet-stream" },
  }


  for i = 1, #methodTbl do
    if strCmp( urlStr, methodTbl[i].url ) == 0 then
      file = FileRead( methodTbl[i].path )
      return file, methodTbl[i].mime
    end
  end


  return nil,""
end


---------------------------------------------
-- method post
---------------------------------------------
function methodPost( urlStr, contents )
  local path = "D:\\temp\\test"  -- 保存先のフォルダーを指定

  path = path .. strReplace( urlStr, "/", "\\" )  -- バックスラッシュを円マークに置換
  Logput( 1, path )

  FileWrite( contents, path )  -- テーブルの内容をファイルに書き込む
end


---------------------------------------------
-- http request status code = 200
---------------------------------------------
function index200( data, mimeType )
  local contentLength = #data
  local contentHeader = 'HTTP/1.1 200 OK\r\n'
    .. 'Server: SocketeEbugger Web Server/ ver.0.1\r\n'
    .. 'Content-Type: ' .. mimeType .. ' charset=UTF-8\r\n'
    .. 'Content-Length: ' .. contentLength
    .. '\r\n'
    .. 'Connection: close\r\n\r\n'

  SendData( contentHeader )
  SendData( data )
end


---------------------------------------------
-- http request status code = 404
---------------------------------------------
function index404()
  local contentHeader = 'HTTP/1.0 404 Not Found\r\n'
    .. 'Connection: close\r\n\r\n'
    .. '<html><head><title>404 Not Found</title></head>\r\n'
    .. '<body>\r\n'
    .. '<h1>Not Found</h1>\r\n'
    .. '</body></html>\r\n'

  SendData( contentHeader )
end


---------------------------------------------
-- http request status code = 400
---------------------------------------------
function index400()
  local contentHeader = 'HTTP/1.0 400 Bad Request\r\n'
    .. 'Connection: close\r\n\r\n'
    .. '<html><head><title>400 Bad Request</title></head>\r\n'
    .. '<body>\r\n'
    .. '<h1>Bad Request</h1>\r\n'
    .. '</body></html>\r\n'

  SendData( contentHeader )
end


---------------------------------------------
-- http request status code = 405
---------------------------------------------
function index405()
  local contentHeader = 'HTTP/1.0 405 Method Not Allowed\r\n'
    .. 'Connection: close\r\n\r\n'
    .. '<html><head><title>405 Method Not Allowed</title></head>\r\n'
    .. '<body>\r\n'
    .. '<h1>Method Not Allowed</h1>\r\n'
    .. '</body></html>\r\n'

  SendData( contentHeader )
end


---------------------------------------------
-- compare string A and string B.
---------------------------------------------
function strCmp( stringA, stringB )
  local strLen = string.len( stringA )

  if strLen ~= string.len( stringB ) then 
    return -1 
  end

  for i = 1, strLen do
    if string.byte( stringA, i ) ~= string.byte( stringB, i ) then
      return -1
    end
  end

  return 0
end

---------------------------------------------
-- compare string A and string B.
---------------------------------------------
function strNCmp( stringA, stringB, n )
  if n <= 0 then return -1 end

  for i = 1, n do
    if string.byte( stringA, i ) ~= string.byte( stringB, i ) then
      return -1
    end
  end

  return 0
end

---------------------------------------------
-- divide string by ts and enter table.
--  https://qiita.com/peg/items/c472f7a7d9fcca1b5cb7
-- https://symfoware.blog.fc2.com/blog-entry-455.html
---------------------------------------------
--[[
function strSplit(str, ts)
  -- 引数がないときは空tableを返す
  if ts == nil then return {} end

  local t = {} ; 
  i=1
  for s in string.gmatch(str, "([^"..ts.."]+)") do
    t[i] = s
    i = i + 1
  end

  return t
end
]]

function strSplit( str, delim )
    -- Eliminate bad cases...
    if string.find(str, delim) == nil then
--        return { str }
        return nil
    end

    local result = {}
    local pat = "(.-)" .. delim .. "()"
    local lastPos
    for part, pos in string.gfind(str, pat) do
        table.insert(result, part)
        lastPos = pos
    end
    table.insert(result, string.sub(str, lastPos))
    return result
end


---------------------------------------------
-- ASCIIテーブルをコード変換後に文字列化
--
-- ASCIIの例
-- 入力: { 0x30, 0x31, 0x32 }
-- 返値: "012"
--  http://amlaid.air-nifty.com/blog/2015/10/serial-debugger.html
-- 0x0D is CR,0x0A is LF
---------------------------------------------
function CharFromTbl(tbl)
  tblChar = {}

  for i=1, #tbl do
    if tbl[i] == 0x0D or 
       tbl[i] == 0x0A then
      tblChar[i] = string.char(tbl[i])
    else
      tblChar[i] = string.char(tbl[i])
    end
  end

  return table.concat(tblChar)
end


---------------------------------------------
-- replace string to tr from ts and enter table.
---------------------------------------------
function strReplace( str, before, after )
  local str,num = string.gsub( str, before, after )
  return str
end



---------------------------------------------
-- get http header from table
-- CR is 0x0D LF is 0x0A
---------------------------------------------
function getHttpHeader( tbl )
  local header = {}
  local body = {}
  local tblLen = #tbl
  local index = 1

  for i = 1, tblLen - 3 do
    if tbl[ i + 0 ] == 0x0D and
       tbl[ i + 1 ] == 0x0A and
       tbl[ i + 2 ] == 0x0D and
       tbl[ i + 3 ] == 0x0A then
       break
    else
      header[i] = tbl[i]
      index = index + 1
    end
  end
  if index > tblLen - 3 then return nil,nil end

  for i = 1, tblLen do
    body[i] = tbl[ i + index + 3 ]
  end

  return header,body
end

※、わたしあれ、正規表現さっぱりですわ!









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

Socket Debuggerを使ってみる! ポート:11!Mime Typeの簡単な実装 [NETWORK]

SocketDebugger
こことか、 https://www.udom.co.jp/sdg/
こことか、 http://sdg.ex-group.jp/lua.html
をまず見てね!

webServer_005.png前回まででは、コンテンツはhtml文として決め打ちでしたが、今回はmime typeを指定していろいろなファイルの転送を可能とします。


webServer_003.pngこれにより、ブラウザの左上にfaviconが表示されるようになったり、


webServer_006.pngCSVファイルのダウンロードなんかもできますね。


変更部分のみ
---------------------------------------------
-- http request
---------------------------------------------
function httpRequest( request )
  local len = #request
  if len < 4 then return 0 end

  local result = 0
  if request[len - 3] == 0x0D and
     request[len - 2] == 0x0A and 
     request[len - 1] == 0x0D and 
     request[len - 0] == 0x0A then
    Logput( 1, 'end of the request message.' )


    local reqStr = CharFromTbl( request )
    local lineTbl = strSplit( reqStr, "\r\n" )
    local line1Tbl = strSplit( lineTbl[1], " " )

    local methodStr = line1Tbl[1]
    local urlStr = line1Tbl[2]
    local httpVersionStr = line1Tbl[3]

    if strCmp( methodStr, 'GET' ) == 0 then
      local fileData = {}
      local mimeType = ""
      fileData, mimeType = methodGet( urlStr, httpVersionStr )
      if fileData ~= nil and mimeType ~= "" then 
        index200( fileData, mimeType )
        result = 200
      else
        index404()
        result = 404
      end
    elseif strCmp( methodStr, 'PUT' ) == 0 then
      index405()
      result = 405
    elseif strCmp( methodStr, 'POST' ) == 0 then
      index405()
      result = 405
    elseif strCmp( methodStr, 'DELETE' ) == 0 then
      index405()
      result = 405
    else
      index400()
      result = 400
    end

  end

  return result
end


---------------------------------------------
-- method get
---------------------------------------------
function methodGet( urlStr, verStr )
  local file = {}
  local methodTbl = 
  {
    { url = "/",           path = "D:\\temp\\index.html", mime = "text/html" },
    { url = "/index.html", path = "D:\\temp\\index.html", mime = "text/html" },
    { url = "/index.htm",  path = "D:\\temp\\index.html", mime = "text/html" },
    { url = "/favicon.ico",  path = "D:\\temp\\favicon.ico", mime = "image/png" },
    { url = "/text.txt",  path = "D:\\temp\\text.txt", mime = "text/plain" },
    { url = "/photo1.jpg",  path = "D:\\temp\\photo1.jpg", mime = "image/jpeg" },
    { url = "/image1.png",  path = "D:\\temp\\image1.png", mime = "image/png" },
    { url = "/image2.bmp",  path = "D:\\temp\\image2.bmp", mime = "image/bmp" },
    { url = "/test.csv",  path = "D:\\temp\\test.csv", mime = "text/csv" },
--    { url = "/test.bin",  path = "D:\\temp\\test.bin", mime = "application/octet-stream" },
  }


  for i = 1, #methodTbl do
    if strCmp( urlStr, methodTbl[i].url ) == 0 then
      file = FileRead( methodTbl[i].path )
      return file, methodTbl[i].mime
    end
  end


  return nil,""
end


---------------------------------------------
-- http request status code = 200
---------------------------------------------
function index200( data, mimeType )
  local contentLength = #data
  local contentHeader = 'HTTP/1.1 200 OK\r\n'
    .. 'Server: SocketeEbugger Web Server/ ver.0.1\r\n'
    .. 'Content-Type: ' .. mimeType .. ' charset=UTF-8\r\n'
    .. 'Content-Length: ' .. contentLength
    .. '\r\n'
    .. 'Connection: close\r\n\r\n'

  SendData( contentHeader )
  SendData( data )
end



※ お役立ちリンク
https://asahi-net.jp/support/guide/homepage/0017.html









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

Socket Debuggerを使ってみる! ポート:10!簡単なWeb Server [NETWORK]

SocketDebugger
こことか、 https://www.udom.co.jp/sdg/
こことか、 http://sdg.ex-group.jp/lua.html
をまず見てね!

と言う訳で先人の知恵を借りて、文字列で処理をする事に。差分のみ

---------------------------------------------
-- http request
---------------------------------------------
function httpRequest( request )
  local len = #request
  if len < 4 then return 0 end

  local result = 0
  if request[len - 3] == 0x0D and
     request[len - 2] == 0x0A and 
     request[len - 1] == 0x0D and 
     request[len - 0] == 0x0A then
    Logput( 1, 'end of the request message.' )


    local reqStr = CharFromTbl( request )
    local lineTbl = strSplit( reqStr, "\r\n" )
    local line1Tbl = strSplit( lineTbl[1], " " )

    local methodStr = line1Tbl[1]
    local urlStr = line1Tbl[2]
    local httpVersionStr = line1Tbl[3]

    if strCmp( methodStr, 'GET' ) == 0 then
      local fileData = methodGet( urlStr, httpVersionStr )
      if fileData ~= nil then 
        index200( fileData )
        result = 200
      else
        index404()
        result = 404
      end
    elseif strCmp( methodStr, 'PUT' ) == 0 then
      index405()
      result = 405
    elseif strCmp( methodStr, 'POST' ) == 0 then
      index405()
      result = 405
    elseif strCmp( methodStr, 'DELETE' ) == 0 then
      index405()
      result = 405
    else
      index400()
      result = 400
    end

  end

  return result
end


---------------------------------------------
-- method get
---------------------------------------------
function methodGet( urlStr, verStr )
  local file = {}
  local methodTbl = 
  {
    { url = "/",           path = "D:\\temp\\index.html" },
    { url = "/index.html", path = "D:\\temp\\index.html" },
    { url = "/index.htm",  path = "D:\\temp\\index.html" },
  }


  for i = 1, #methodTbl do
    if strCmp( urlStr, methodTbl[i].url ) == 0 then
      file = FileRead( methodTbl[i].path )
      return file
    end
  end


  return nil
end


---------------------------------------------
-- divide string by ts and enter table.
--  https://qiita.com/peg/items/c472f7a7d9fcca1b5cb7
---------------------------------------------
function strSplit(str, ts)
  -- 引数がないときは空tableを返す
  if ts == nil then return {} end

  local t = {} ; 
  i=1
  for s in string.gmatch(str, "([^"..ts.."]+)") do
    t[i] = s
    i = i + 1
  end

  return t
end


※ お役立ちリンク
https://qiita.com/peg/items/c472f7a7d9fcca1b5cb7
https://symfoware.blog.fc2.com/blog-entry-455.html
※ ともすれば変数がテーブルなのか?文字列なのか?それとも他の型なのか判らなくなってくるので、変数名、、、。










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

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