ESPr Developerを使って、インターネットサイコロを作る。

インターネットダイス概要!?
ESPr Developerを使って、インターネットダイス(サイコロ)を作ってみます。サイコロを転がし、出た目の値をインターネット経由で自動取得し、OLED(表示器)に値を表示します。以下に製作したインターネットダイスの動画を紹介します。

仕組は、サイコロに内に組み込んだESPr Developerから、I2C接続で加速度センサー(MMA8451)のセンサー値を読み取り、サイコロの向きを判定します。判定結果をWiFiアクセスポイント、インターネット経由でレンタルサーバー上のPHPプログラムを使って、テキストファイルに書き込みます。
動画左上にあるOLED(表示器)もESPr Developerで制御していて、サイコロ内のESPr Developerが書き込んだサーバー上のテキストファイルを定期的に読み込み、読み込んだ値によって表示する内容を変更します。システム構成イメージ図を示します。

インターネットダイスの構成
サイコロの値の表示器とインターネットダイスの写真です。表示器はOLED表示器(写真左)として既に説明しているので詳細は省略します。インターネットダイス(写真右)には、サイコロ値を検出する為の加速度センサーを接続するESPr Developerモジュールを内部に含んでいます。

次の写真が、ESPr Developer、加速度センサー、電池、その他部品をモジュール化したものです。100円均一で購入したケースに穴を開けて、全電子部品接続した基板と電池を固定します。基板は樹脂スペーサーを使ってネジ固定,電池入れはケースに直接木ねじで固定しています。電池から基板への電源供給は基板側をピンソケットにして、電池からのピン挿入で抜き差し出来る様にしています。このモジュールに段ボール、スポンジ等をつけて、ビニールテープ等で固定しサイコロ形状にし、パソコンで印刷した番号を両面テープで貼り付けます。正直あまりうまくできておらず、形も歪んでいますし、出やすい目など偏ります。もし、作られる場合は、工夫してみて下さい。
また、電子部品ですので、あまり強く投げることは好ましくありませんので、大きすぎたり角部が固いとほとんど転がりません。出来るだけコンパクトにスポンジ等を使用して製作します。

配線・接続
配線・接続について説明します。今回、サイコロ内にESPr Developerを入れることから、電池で動作する様にする必要があり、5V出力昇圧DCDCコンバータを使用しました。0.9~5V入力に対し、5V出力する仕様ですが、ESPr Developerの消費電流も考慮した電圧設定が必要と思われます。今回は手持ちの電池ケースが単3X3本でしたのでそのまま利用しました。サイコロの姿勢からサイコロの目を検出する為に使用する加速度センサー(MMA8451)は、電源電圧が5Vと思い込んでいたのでI2Cの電圧レベル変換が必要と考え、I2C電圧変換モジュールを接続し、ESPr® Developer側、加速度センサー側、それぞれにプルアップ抵抗を入れています。ただ、この記事を書く際に仕様を確認するとESPr® Developerと同じ3.3Vにも対応している様ですので、I2C電圧変換モジュールは不要だったかもしれません。とりあえず動いたので良いのですが、少し残念です。

ESPr Developerと加速度センサーは、ユニバーサル基板には直接はんだづけせずに、下の写真の通り、ピンヘッダ/ピンソケットで脱着できる様にしました。加速度センサーは、ピンソケットに挿入しただけですと、ガタツキがありましたので手持ちの樹脂スペーサーを削って高さ調整し、M2.5ネジで固定しています。

配線・接続は次の写真の通りです。Arduino UnoではI2C用の信号端子が予め設定されていましたが、ESPr Developerではプログラム上で指定します。今回は12,14番ピンをそれぞれSDA,SCL信号端子に設定します。また、先ほども記載の通り③のI2C電圧変換モジュールは不要かもしれません。その場合、ESPr Developerからの3.3Vを加速度センサーに供給するなど、配線の見直しが必要となります。

インターネットダイス内のESPr Developerプログラム
サイコロ内のESPr Developerプログラムは次の通りです。
基本的には、Arduino Unoで加速度センサー値を取得したプログラムとESPr® Developerでインターネット接続した時のプログラムを組み合わせたものです。

#include <ESP8266WiFi.h>
#include <Wire.h>

// WIFI関連
const char* ssid = "アクセスポイントSSID";             // 接続アクセスポイントのSSID         
const char* password = "アクセスポイントパスワード";     // 接続アクセスポイントのパスワード   
const int httpPort = 80 ;                       // ポート番号指定
const char* host = "接続サーバードメイン名";    // 接続サーバードメイン名
const char* path = "プログラム名";              // 呼び出すプログラム名
const int s_p = 4;                              // lolli:4
int loopCnt=0;
int rst_Cnt=0; 

String body = "OK";
String rtn ="";
String dic_vlu_bef = "" ;

// I2C-加速度センサー
#define DVC_ADRS 0x1D                           // デバイスアドレス
#define GRAV_E       (9.80665F)                 // 標準重力加速度

uint16_t x_r , y_r , z_r ;                      // XYZ 14bitデータ
long int x_v , y_v , z_v ;                      // XYZ 数字
float x_g , y_g , z_g ;                         // XYZ 角加速度


// ①通信開始、デバイス初期化
bool DVC_INIT(uint8_t i2caddr){
    Wire.begin(12,14);                          // 接続開始(SDA:12pin,SCL:14pin)
  
    wrt_reg(0x2B, 0x40);                        // DEVICE RESET ENABLED
    while (read_reg(0x2B) & 0x40);              // デバイスリセット待ち
    wrt_reg(0x0E , 0x01);                       // RANGE 設定
  
    wrt_reg(0x2B, 0x02);                        // PowerMode
    wrt_reg(0x2D, 0x01);                        // INT_EN_DRDY
    wrt_reg(0x2E, 0x01);                        // INT_CFG_DRDY

    wrt_reg(0x11, 0x40);                        // PL_EN
    wrt_reg(0x2A, 0x01 | 0x04);                 // ACTIVE , LNOISE
    return true;   
}

// ②指定レジスタにデータ書き込み
void wrt_reg(uint8_t reg, uint8_t value) {
    Wire.beginTransmission(DVC_ADRS);           // デバイス指定、通信開始
    Wire.write((uint8_t)reg);                   // レジスタ指定
    Wire.write((uint8_t)(value));               // データ書込
    Wire.endTransmission();                     // 送信完了
}

// ③指定レジスタからデータを読み出し
uint8_t read_reg(uint8_t reg) {
    Wire.beginTransmission(DVC_ADRS);           // 送信処理開始
    Wire.write(reg);                            // レジスタ指定
    Wire.endTransmission(false);                // 送信完了(コネクション維持)
    Wire.requestFrom(DVC_ADRS, 1);              // 1byteデータに要求

    if (! Wire.available()) return -1;          // データ有無判定
    return (Wire.read());                       // 1byteデータ  
}

// ④データ取得、変換
void read_data(void) {
    Wire.beginTransmission(DVC_ADRS);           // 送信処理開始
    Wire.write(0x01);                           // アドレス指定
    Wire.endTransmission(false);                // 送信完了(コネクション維持)
    Wire.requestFrom(DVC_ADRS, 6);              // 6byteデータ要求
                                                //
    // データ取得、変換
    x_r = Wire.read() ; x_r <<= 8 ; x_r |= Wire.read() ; x_r >>= 2;
    y_r = Wire.read() ; y_r <<= 8 ; y_r |= Wire.read() ; y_r >>= 2;
    z_r = Wire.read() ; z_r <<= 8 ; z_r |= Wire.read() ; z_r >>= 2;

    x_v = (long int)x_r ; y_v = (long int)y_r ; z_v = (long int)z_r ;    

    // マイナス値変換
    if(x_r & 0x2000) x_v = x_v - 16384 ;        
    if(y_r & 0x2000) y_v = y_v - 16384 ;
    if(z_r & 0x2000) z_v = z_v - 16384 ;
 
    // スケールレンジを取得
    uint8_t range = read_reg(0x0E) & 0x03;
    uint16_t div_p = 1;

    // 加速度変換係数(測定レンジにより異なる)
    if (range == 0x02) div_p = 1024 ; 
    if (range == 0x01) div_p = 2048 ;
    if (range == 0x00) div_p = 4096 ;
    x_g = (float)x_v * GRAV_E / div_p ; 
    y_g = (float)y_v * GRAV_E / div_p ; 
    z_g = (float)z_v * GRAV_E / div_p ;
}

// ⑤最初に1回だけ行う処理
void setup() {
    Serial.begin(115200);
    // デバイスアドレス指定し、初期設定
    if(!DVC_INIT(DVC_ADRS))  {
        Serial.println("-----");
        while (1);
    }
                                                  
    uint8_t reg1 = read_reg(0x2A);              // 元設定確認
    wrt_reg(0x2A, 0x00);                        // StandByMode
    wrt_reg(0x0E, 0x00 & 0x3);                  // Set Range( 0x00:2g , 0x01:4g , 0x02:8g , 0x03:reserved )
    wrt_reg(0x2A, reg1 | 0x01);                 // ActiveMode

    // ◆ WIFI-初期設定 ◆
    Serial.println() ; Serial.println() ;
    Serial.print("Connecting to ") ; Serial.println(ssid);

    WiFi.mode(WIFI_STA);                         // STAモード設定
    WiFi.begin(ssid, password);                  // SSID,パスワードを指定してアクセスポイント接続   

    // Wi-Fi接続確認
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }

    Serial.println("");
    Serial.println("WiFiに接続しました。"); 
    Serial.println("");
}

// ⑥繰り返し処理(センサーを読んで、シリアル通信出力)
void loop() {
    float jdg_vlu ;
    String dic_vlu = "9" ;

    delay(1000);
    read_data();                                // XYZ軸データ取得

    //姿勢判定 → サイコロの目判定
    jdg_vlu = 0.7071 * GRAV_E ;                 // 姿勢判定基準
    if(z_g > jdg_vlu){
        dic_vlu="1" ;
    }else if(z_g  < -1 * jdg_vlu){
        dic_vlu="6";
    }else{
        if(y_g > jdg_vlu){
            dic_vlu="5" ;
        }else if(y_g  < -1 * jdg_vlu){
            dic_vlu="2";
        }else{
            if(x_g > jdg_vlu){
                dic_vlu="4" ;
            }else if(x_g  < -1 * jdg_vlu){
                dic_vlu="3" ;
            }else{
                dic_vlu="0" ;
            }
        }
    }
    
    if (dic_vlu != dic_vlu_bef || rst_Cnt > 15){
        // サイコロの目が変化した場合、
        // もしくは先回書き込み後、15回以上ループした場合に書き込み処理を行う。
        rst_Cnt=0;
        loopCnt = loopCnt + 1 ;
        body = dic_vlu ;
        dic_vlu_bef = dic_vlu ;
        
        WiFiClient client;                           // TCP接続用にWiFiClient classを使用する

        // TCP接続確認
        if (!client.connect(host, httpPort)) {       // 外部サーバーに接続
            Serial.println("接続に失敗しました。");
            return;
        }

        // サーバにリクエストを送信(POST)
        client.print(String("POST ") + path + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" + 
               "Content-Length:" + String(body.length())+ "\r\n" + 
               "Connection: close\r\n\r\n" +
               body);
   
        // リクエスト結果受信待ち            
        unsigned long timeout = millis();
        while (client.available() == 0) {
            if (millis() - timeout > 12000) {
                Serial.println(">>> タイムアウトしました。");
                client.stop();
                return;
            }
        }

        // HTTPヘッダを含むデータの読み取り(1文字毎) 
        String raw = "";
        while (client.available()) {
            char c = client.read();
            raw = raw +c;
        }  

        // HTTPヘッダの削除、
        int index = raw.indexOf("\r\n\r\n");              // 改行が2回続く箇所検索(\r\n(='CR+LF'))
        rtn = raw.substring(index+s_p , index+s_p +1 ) ;  // ( index + s_p +2 )から1文字切り出す        
    }
    
    rst_Cnt = rst_Cnt + 1 ;
}

ここではサイコロの目の判定について簡単に説明します。Arduino Unoで加速度センサー評価時にセンサー値をX・Y・Z方向の加速度に変換しています。この加速度値によって、加速度センサーの姿勢(=サイコロの目)判定を行います。プログラム行番135~155が判定プログラムになります。センサーから得られるある軸方向の加速度値が、標準重力加速度の約7割程度の時におよそ45度傾いているとのことなので、この値を判定値とします。Z軸,Y軸,X軸の順番でそれぞれ水平/直角/裏向きの判定を行います。Z軸が水平(>0.7 X 標準重力加速度)もしくは裏向き(< -0.7 X 標準重力加速度)判定時には、サイコロ上面の値が確定し、判定処理を終了します。直角判定となった時には次の軸に同様の処理を行います。
この様な処理の結果、サイコロの目が先回と異なる、もしくは一定回数以上同じ値が継続した場合にサーバー上のPHPプログラムにサイコロの値をPOST送信します。(行番158~199)

 

サーバー上のPHPプログラム
レンタルサーバー上のPHPプログラムを示します。行番6でESPr DeveloperからPOST送信されたサイコロの値を受け取ります。行番9では、行番3で取得した日付と受け取ったサイコロの値をカンマ  “ , ”  で区切ってテキストファイルに上書保存します。

<?php
	// 日時取得
	$timestamp = date('Y-m-d H:i:s');

	// post送信のBodyすべて取得
	$entireBody = file_get_contents('php://input');		

	// 取得内容をファイルに書込
	file_put_contents("DICE_HISTORY.txt",$timestamp.','.$entireBody);
	
	echo $entireBody ;
?>

書き込まれたテキストファイルをメモ帳で開くと次のようになります。日付   時刻の後に  ‘ , ’  で区切ってサイコロの値「5」が書き込まれています。日付と時刻は書き込む様にしてみたものの実際には利用していません。


OLED表示器側のESPr Developerプログラム

サイコロの値を表示するESPr Developerプログラムは次の通りです。先日、スマホから入力した文字列をESPr Developerに接続するOLED表示器にスクロール表示するプログラムを作りましたが、大体同じです。ただ、その時のプログラムでは文字列をサーバー側で16進数に変換しましたが、今回は文字列(サイコロの値)をそのままテキストファイルに書き込み、OLED表示器側のESPr Developerプログラム内でテキストファイルの値によって、プログラム内に予め書き込んである16進数の値を設定・表示します。
具体的には行番86でサーバーからテキストファイルの内容を取得し、改行コード等を除去したあと、行番96で カンマ  ‘ , ’  の次に書かれたサイコロの値を取得します。 行番97~111でサイコロの値によって、16進数表記の表示内容を変数に設定し、この後の処理でOLED表示器に内容を表示します。
既に一度作成していますが、文字を16進数に変換するプログラム作成を少し面倒に感じ、手を抜いてしまったのが実情です。ただ、一応表面的な目的は達成していると思います。

#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 chk_cnt=0;
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 = "" ;
    String dice_value = "8" ;
    // 先回、サーバー接続時間からの経過時間計算
    tim_dif = micros() - tim_bef;

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

        // 読み込んだ内容の改行コードを除去
        rtn_server.replace("\r\n", "");
        rtn_server.replace("\r", "");
        rtn_server.replace("\n", "");
        rtn_server.replace(" ", "");
        rtn_server.replace(" ", "");
        //Serial.println(rtn_server.charAt(1+rtn_server.indexOf(",")));
        
        dice_value = rtn_server.charAt(1+rtn_server.indexOf(",")) ;
        if (dice_value=="1"){
            rtn_server="0000000000000000100008400440FE7F0040004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100008400440FE7F004000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
        } else if (dice_value=="2"){
            rtn_server="00000000000018400C6006700258024C024602438241C4407840000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018400C6006700258024C024602438241C44078400000000000000000000000000000000000000000000000000000000000000000000000000000";
        } else if (dice_value=="3"){
            rtn_server="00000000000008400C408440864082408240826082204431381E000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008400C408440864082408240826082204431381E0000000000000000000000000000000000000000000000000000000000000000000000000000";
        } else if (dice_value=="4"){
            rtn_server="0000000000078005C0046004300418040C440644FE7F0044004400040000000000000000000000000000000000000000000000000000000000000000000000000000000000078005C0046004300418040C440644FE7F004400440004000000000000000000000000000000000000000000000000000000000000000000000000";
        } else if (dice_value=="5"){
            rtn_server="000000000000FE4182404240424042404260422042308218020F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FE4182404240424042404260422042308218020F0000000000000000000000000000000000000000000000000000000000000000000000000000";
        } else if (dice_value=="6"){
            rtn_server="000000000000C00F703998204C4044404640424082208231001F0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000C00F703998204C4044404640424082208231001F0000000000000000000000000000000000000000000000000000000000000000000000000000";
        } else {
            rtn_server="04040403E47FBC408440847F00019C00C4FF74925F92C4FF6492549244921C8008002AF92A892A892AF9080000C08A70DA007278DE86028C8280E2E03E1800300000F00710041004100410041004FFFF100410041004100410041004F00700000000000000000000000000000000000000000000000000000000000000000000";
        }

        
            // 変数初期化(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を電池を使って動かしてみました。スマホのテザリング機能等を利用すれば、野外でのちょっとした連続計測などもできそうです。いろいろな応用方法を検討してみたいと思います。

 

「ESPr Developerを使って、インターネットサイコロを作る。」への1件のフィードバック

コメントを残す

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