ESPr Developerを使ってスマホからOLEDを制御する

スマホでOLED制御(概要)
これまでの実験を応用し、スマホで入力した内容をOLEDに表示させたいと思います。③インターネット回線接続のところで確認した方法を少し発展させ実現しました。
スマホ側で任意の文字を入力し、レンタルサーバー上のPHPプログラムにPOST送信します。PHPプログラムは、送信された文字列をOLED(WS0010)に書き込める16進数に変換し、結果をサーバー上のテキストファイルに書き込みます。ESPr® Developerから、一定の時間間隔でテキストファイルの内容を読み込み、読み込んだ内容をOLEDに書き込みます。
配線は「④OLED WS0010」と同じです。

前の‘④OLED WS0010’では、OLEDの書き込みデータとして、「0xFF , 0x23 , 0x7E・・・」の様に記載しましたが、同じ表記では、ESPr® Developer側のプログラムでテキストファイルから読み取った文字列データを私のスキルでは、うまく個々の数値データに変換し、配列に格納することが出来ませんでした。
そこで、カンマと‘0x’の部分を除去したものをテキストデータに保存し、ESPr Developer側は、データを前から2文字づつ処理して数値に変換することとしましした。通信データ量も減るので、よしとしました。

出来たもの
次の動画の通り、私が想定していたものは出来ました。スマホ入力内容は携帯回線を使用しインターネット経由で、OLEDの表示内容を変更出来ます。約10秒に1回の頻度でレンタルサーバーのデータを取得しますが、スクロール速度をあまり早くしなければデータ読み取り時の遅延はそんなに感じないと思います。
動画内での携帯への文字入力、送信ボタンのクリックなどはBluetooth マウス,キーボードを使用しています。

スマホ(入力端末)側PHPプログラム
スマホ等の入力端末側ブラウザの出力を行うPHPプログラムは以下の様な感じです。行番23が文字列入力部で、行番26が送信ボタンとなっています。送信ボタンをクリックすると自身のプログラムに入力データをPOST送信します。入力情報有無を行番32で判定し、入力データがある場合、行番35で入力されたデータを1文字づつ分割して配列($result)に格納します。
行番39~67では、予め作成してあるフォント情報ファイルからフォント情報を読み出し、いったん配列に格納します。先回、‘変換プログラムブラウザ版’を紹介したのですが、元のフォント情報は同じものです。
次に行番69~84で配列に格納された文字を1文字づつ、配列のフォント情報と照合し、OLED書き込み情報を取得します。前にも書きました通り、ESPr® Developer側でのデータ処理がうまく出来なかった為、行番78,79でカンマと‘0x’の部分を除去します。すべての文字の照合結果を行番88でファイルに書き込みます。以上が大凡のプログラム構成です。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>OLED(WS0010) 文字書き込み</title>

<style type="text/css">
body,td,th {font-family:"MS P明朝", "細明朝体", "ヒラギノ明朝 Pro W3";font-size:38px;}
input.INP00 {background-color: #FFFF99;}
input.BTN00 {background-color: #00ffff;}
</style>

</head>


<body>
    <?php
      $inp_info = '' ;
      $inp_info = htmlspecialchars($_POST['comment']) ;

      echo '<h1>OLED 文字書き込み</h1>';
      echo '<form action = "" method = "post">';
      echo '<input type = "text" class="INP00" size="30" style="font-size:48px;" name ="comment" value ="'.$inp_info.'" >';
      echo '<BR>';
      echo '<BR>';
      echo '<input type = "submit" class="BTN00" style="font-size:70px;" value ="送信" >';
      echo '</form>';
      echo '<BR>';
      echo date( "Y年m月d日 H時i分s秒" ) ;
      echo '<BR>'; echo '<BR>';
      
      if ($inp_info != ''){
	// 入力有無判定→入力有り
	// 入力データを1文字単位で配列に格納する
      	$result = preg_split("//u",$inp_info, -1, PREG_SPLIT_NO_EMPTY);
	//for ($i = 0 ; $i < count($result); $i++) { echo $result[$i]; }
	
	// フォントファイルを開いて情報取得する
        $file=fopen("font_sample.txt","r");
	$chr_file_out = "" ;

        if ($file){
	    $lin_cnt = 0 ;
	    // フォントファイルから1行づつデータ取得する
	    while($line=fgets($file)){
            	$line = mb_convert_encoding($line,'UTF-8','sjis-win');
	        // カンマ区切データを分割し配列に格納
		$ary_chr=explode(',',$line);

	    	for($i = 0 ; $i < count($ary_chr); $i++){
		    $ary_chr[$i]=trim($ary_chr[$i]);
		    if($i == 0){
			// 主文字部の取得
		        $g_inf_00[$lin_cnt] = $ary_chr[$i] ;

		    }elseif($i >= 36){
			// 16進数文字部取得(縦形式用のデータのみ取得)
			// 出力データ(文字列)に変換
		        $g_inf_01[$lin_cnt] = $g_inf_01[$lin_cnt].$ary_chr[$i] ;
		    	if ($i < count($ary_chr)-1){
		    	    $g_inf_01[$lin_cnt] = $g_inf_01[$lin_cnt].',' ;
		        }
		    }
	        }
		
		$lin_cnt=$lin_cnt+1 ;
	    }
	    
	    // 入力文字を1文字ずつフォント情報に照合する
	    for ($i = 0 ; $i < count($result); $i++) { 
		for($j = 0 ; $j < count($g_inf_00); $j++){
			$chk_chr = mb_substr($g_inf_00[$j],1,1);

			// 入力文字とフォント情報が一致
			if($result[$i] == $chk_chr){
				// 元々は16進数表記予定も変更により下記処理追加
				// カンマと'0x'を除去する
				$tmp = str_replace(",","", $g_inf_01[$j]);
				$tmp = str_replace("0x","", $tmp);
				$chr_file_out=$chr_file_out.$tmp;
				break ;
			}
		}
	    }
        }
	
	// 照合・変換結果をファイルに書き込む
	file_put_contents("sample.txt",$chr_file_out);

      }else{
		//データがインプットされていない場合
	      	echo 'データ入力されていません。';	
		echo '<BR>';
      }
  
    ?>
  </body>
</html>

ESPr Developer側のプログラム
次にESPr Developer側のプログラムを示します。
行番5,6ではアクセスポイントへの接続SSID,パスワードを設定しています。行番8~12はSPI接続のIOエキスパンダ用、行番13~16はOLED制御用のESPr® Developerピンアサインを設定します。
行番18は電源投入時の画面リセット用データ,行番20はOLED書き込み用の値を設定する為の配列,レンタルサーバーのデータを読み込むことにしたので実際は使用していませんが、行番22は表示用データです。
行番41~51はWiFi接続、行番54はIOエキスパンダ初期化関数呼び出し,行番55はOLED初期化関数呼び出しです。
行番61~68は、OLED書き込み用配列 dsp_OLED に dsp_info の値を移しています。
行番75では、先回サーバー接続時からの経過時間を計算し、行番78で判定し、10秒以上経過している場合はサーバーのデータを行番84(関数呼び出し)で読み込みします。
行番87~89では、改行コード除去,行番92~94ではOLED書き込み用配列の初期化,行番97~107で読み込んだデータを数値変換し、OLED書き込み用配列に設定します。
行番118~126では、OLED書き込み用配列の値をOLEDに設定し表示を変更します。

#include <SPI.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>

char ssid[] = "アクセスポイントSSID";
char password[] = "アクセスポイントパスワード";

// GPIOエクスパンダ MCP23S08 のSPIインターフェースピン設定
const uint8_t sclk = 14;            // SPIクロック出力ピン
const uint8_t mosi =13;             // Master Output Slave Input ESP8266=Master,MCP23S08=slave 
const uint8_t MCP_CS = 0;           // MCP23S08 CSピン(Chip Select)
const uint8_t MCP_RST_pin = 16;     // MCP23S08 リセットピン
// OLED ピン設定 (8bitモード用)
const uint8_t OLED_RS_pin = 2;      // 設定コマンド or データコマンドのモード選択ピン
const uint8_t OLED_RW_pin =  5;     // 書込み or 読み取りモード選択ぴん
const uint8_t OLED_E_pin = 4 ;      // データ確定のEnterピン

const byte init_wrt[]={0x00,0xFF};  // 立ち上げ時、LEDテスト表示

byte dsp_OLED[1000];

byte dsp_info[]={0x00,0x00,0x00,0x40,0x04,0x40,0x04,0x60,0x04,0x30,0x04,0x18,0x04,0x0C,0x04,0x06,
                 0x04,0x03,0x84,0x07,0xE4,0x0C,0x3C,0x18,0x00,0x30,0x00,0x60,0x00,0x40,0x00,0x00,
                 0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x40,0xC0,0x40,0x60,0x60,0x38,0x20,0x4F,0x30,
                 0xC8,0x18,0x88,0x0D,0x08,0x06,0x88,0x03,0xE8,0x00,0x38,0x00,0x00,0x00,0x00,0x00,
                 0x00,0x00,0x40,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,
                 0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x00,0x00,
                 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x7F,0x40,0x00,
                 0x40,0x00,0xC0,0x00,0x80,0x00,0x80,0x01,0x00,0x01,0x00,0x01,0x00,0x00,0x00,0x00};

int i;
int j;
int lp_cnt=0;
int loopCnt=0;
unsigned long tim_bef=0;
unsigned long tim_dif=0;   
int chr_len  = sizeof(dsp_info) / sizeof(byte) / 2;

// ◆初回動作◆
void setup() {
    Serial.begin(115200);
    Serial.println("");

    WiFi.mode(WIFI_STA);
    WiFi.begin(ssid, password);

    while (WiFi.status() != WL_CONNECTED) {
        Serial.print(".");
        delay(200);
    }
    Serial.println("connected!");
    
    delay(500);                     // 動作が安定するまで0.5秒待つ
    MCP23S08_Ini();                 // GPIOエクスパンダ初期化  
    OLED_Graphic_Ini();             // OLED初期化
    disp_all_init(1); delay(100); disp_all_init(0); delay(100); 
    disp_all_0000(1); delay(100); disp_all_0000(0); delay(100); 

    tim_bef = micros();

    // 配列長さを計算で求める
    for(i=0; i<1000; i++){
        if(i < 2 * chr_len){
            dsp_OLED[i]=dsp_info[i] ;
        }else{
            dsp_OLED[i]= 0x00 ;
        }
    }
}

// ◆繰り返し動作
void loop() {
    String rtn_server = "" ;
    // 先回、サーバー接続時間からの経過時間計算
    tim_dif = micros() - tim_bef;

    // 10秒以上経過していたら、サーバーに接続(10000000=10秒)
    if (tim_dif > 10000000 || loopCnt == 0){
        // 経過時間を計測する為に現在時間を設定
        tim_bef = micros();
        
        loopCnt = loopCnt + 1 ;
        // サーバーからテキストファイルの内容を読み込む
        rtn_server = getServerTextInfo();

        // 読み込んだ内容の改行コードを除去
        rtn_server.replace("\r\n", "");
        rtn_server.replace("\r", "");
        rtn_server.replace("\n", "");

        // 変数初期化(0x00を設定)
        for(i=0; i<1000; i++){
            dsp_OLED[i]=0x00;
        }

        // 2文字で1データ→データ数を計算
        int chr_num = rtn_server.length() / 2 ;
        for(i=0; i<chr_num; i++){
            // tmp_txt_01:1文字目,tmp_txt_02:2文字目
            char tmp_txt_01 = rtn_server.charAt(2*i);
            char tmp_txt_02 = rtn_server.charAt(2*i+1);
            // 文字を数値に変換する
            byte rtn_num = chr_to_hex(tmp_txt_01,tmp_txt_02);

            // OLED表示用変数に設定
            dsp_OLED[i] = rtn_num;
        }

        // 2データで縦1列分なので、2で割って長さを求める
        chr_len = chr_num / 2 ;
        delay(1) ;  
        
    }else{
        delay(25) ;
    }

    // OLED表示設定(lp_cnt数で表示をシフトさせスクロールさせる)
    for(i=0; i<100; i++){
        if(i + lp_cnt < chr_len){
          OLED_XYset(i, 0); OLED_DataWrite(dsp_OLED[ 2 * ( i + lp_cnt ) ]);
          OLED_XYset(i, 1); OLED_DataWrite(dsp_OLED[ 2 * ( i + lp_cnt )  + 1 ]);
        }else{  
          OLED_XYset(i, 0); OLED_DataWrite(dsp_OLED[ 2 * ( i + lp_cnt - chr_len) ]);
          OLED_XYset(i, 1); OLED_DataWrite(dsp_OLED[ 2 * ( i + lp_cnt - chr_len)  + 1 ]);
        }
    }
  
    lp_cnt = lp_cnt + 1 ;
    if(lp_cnt > chr_len){ lp_cnt=0; }
}

// ◆◆◆◆◆◆◆◆  16進文字を数値に変換  ◆◆◆◆◆◆◆◆◆◆
byte chr_to_hex(char txt_01, char txt_02){
    int num_01;
    int num_02;

    if(txt_01 > 47 && txt_01 < 58){
        num_01 = txt_01 - 48;
    } else if (txt_01 > 64 && txt_01 < 71) {
        num_01 = txt_01 - 55;
    } else {
        num_01 = 0 ;
    }
    
    if(txt_02 > 47 && txt_02 < 58){
        num_02 = txt_02 - 48 ;
    } else if (txt_02 > 64 && txt_02 < 71) {
        num_02 = txt_02 - 55 ;
    } else {
        num_02 = 0;
    }

    return 16 * num_01 + num_02 ;
}

// ◆◆◆◆◆◆◆◆◆◆  WiFi関連関数  ◆◆◆◆◆◆◆◆◆◆◆◆◆
// WiFi接続を遮断する
void disconnectWifi() {
    WiFi.disconnect();
    Serial.println("disconnected!");
}

// ◆◆◆◆◆ サーバーからTEXTファイルを読み込む ◆◆◆◆◆◆
String getServerTextInfo() {

    HTTPClient http;
    http.begin("テキストファイルのURL");
    
    int httpCode = http.GET();
    String rtn = "";
    String httpErr = "";
    
    if (httpCode < 0) {
        httpErr = http.errorToString(httpCode);
        rtn =  "ERROR_" + httpErr;
    } else if (http.getSize() < 0) {
        rtn =  "ERROR_SIZE_INFO";
    } else {
        rtn = http.getString();
    }

    http.end();
    return rtn;
}

// ◆◆◆◆◆◆◆◆◆◆  OLED関連関数  ◆◆◆◆◆◆◆◆◆◆◆◆◆
// 画面全初期化(起動時に画面を全点灯,全消灯するロジック)
void disp_all_init(int md){
    for(i=0; i<100; i++){
        OLED_XYset(i, 0); OLED_DataWrite(init_wrt[md]);
        OLED_XYset(i, 1); OLED_DataWrite(init_wrt[md]);
    }
}

// 表示領域(両端)確認用
void disp_all_0000(int md){
    OLED_XYset(0, 0); OLED_DataWrite(init_wrt[md]); OLED_XYset(0, 1); OLED_DataWrite(init_wrt[md]);
    OLED_XYset(99, 0); OLED_DataWrite(init_wrt[md]); OLED_XYset(99, 1); OLED_DataWrite(init_wrt[md]);
}

// GPIOエクスパンダ (MCP23S08)初期化
void MCP23S08_Ini(){ 
    pinMode(MCP_CS, OUTPUT);
    pinMode(MCP_RST_pin, OUTPUT);
   
    SPI.begin();
    SPI.setFrequency(10000000);   
    SPI.setDataMode(SPI_MODE0);
 
    digitalWrite(MCP_RST_pin, HIGH);
    delay(100);
    digitalWrite(MCP_RST_pin, LOW);
    delay(100);
    digitalWrite(MCP_RST_pin, HIGH);
    delay(100);
    digitalWrite(MCP_CS, LOW);
    delay(1);
    
    SPI.write(B01000000);         
    SPI.write(0x05);             
    SPI.write(B00100000);        
    delay(1);
    
    digitalWrite(MCP_CS, HIGH);
    digitalWrite(MCP_CS, LOW);
  
    delay(1);
    SPI.write(B01000000);         
    SPI.write(0x00);              
    SPI.write(B00000000);         
    delay(1);
    
    digitalWrite(MCP_CS, HIGH);
}

// OLED WS0010 初期化
void OLED_Graphic_Ini(){ 
    pinMode(OLED_RW_pin, OUTPUT);
    pinMode(OLED_RS_pin, OUTPUT);
    pinMode(OLED_E_pin, OUTPUT);
   
    digitalWrite(OLED_E_pin, LOW);
    digitalWrite(OLED_RW_pin, LOW);
    digitalWrite(OLED_RS_pin, LOW);
 
    // OLEDリセット
    delayMicroseconds(10);
    OLED_CommandWrite(B00000001);  
    delay(10);
    OLED_CommandWrite(B00010011);  
    delay(10);                     
    OLED_CommandWrite(B00001000);   
    delay(1000);
    
    // ディスプレイ初期化設定
    OLED_CommandWrite(B00011111);  
    delay(10);
    OLED_CommandWrite(B00111000);   
    delay(10);
    OLED_CommandWrite(B00001111);  
    delay(10);
    OLED_CommandWrite(B00000001);   
    delay(10);
    OLED_CommandWrite(B00000010);   
    delay(10);
    OLED_CommandWrite(B00000110);  
    delay(10);
}

// 座標指定
void OLED_XYset(uint8_t x, uint8_t y){
    OLED_CommandWrite(0x80 + x);  
    OLED_CommandWrite(0x40 + y);
}

// コマンド書き込み
void OLED_CommandWrite(uint8_t b){
    digitalWrite(OLED_RS_pin, LOW);
    MCP23S08_SpiCommandWrite(b);
    digitalWrite(OLED_E_pin, HIGH); //EピンHIGH→LOWで完了
    digitalWrite(OLED_E_pin, LOW);
}

// データ書き込み
void OLED_DataWrite(uint8_t b){
    digitalWrite(OLED_RS_pin, HIGH);
    MCP23S08_SpiCommandWrite(b);
    digitalWrite(OLED_E_pin, HIGH); //EピンHIGH→LOWで完了
    digitalWrite(OLED_E_pin, LOW);
}

// SPIコマンド書き込み
void MCP23S08_SpiCommandWrite(uint8_t b){
    digitalWrite(MCP_CS, LOW);
    
    SPI.write(B01000000);
    SPI.write(0x0A);
    SPI.write(b);

    digitalWrite(MCP_CS, HIGH);
}

まとめ
これまでの実験でインタネット上のレンタルサーバー経由で情報交換や簡単な制御が出来ることを確認できました。今のところ検討中ですがESPr Developer側で温度等の計測を行いスマホ等から、状態を監視する様なプログラムも作ってみようかなと思っています。(未確定)

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です