見出し画像

【GASでIoT】スプレッドシートの情報をラズベリーパイPico Wを使って7セグLEDに表示させる(最終)ArduinoIDEでのPico Wのスケッチをご紹介

WEBベースのアプリであるGoogleスプレッドシートや、マクロ言語Google Apps Script(GAS)のメリットの一つに、ネットにつながるデバイスと連携しやすいことが挙げられます。

このメリットを活かして、スプレッドシートの内容を、ネット経由で「ラズベリーパイPicoW」で受け、7セグメントLED表示にチャレンジしたのが以下の記事になります。

7セグ表示器の表示には、代表的なドライブICであるMAX7219を使用しています。



このICは、多くのものが上の様に8桁表示のデバイスとして販売されていますが、4桁や2桁に分けて表示できた方が良いので、配線をつなぎ直して他の桁での表示にもチャレンジしました。

カレンダーの日付など、日常レベルの表示用途は4桁や2桁のものが多そうなので、今後役に立つはずです。

今回の記事は、以上の表示システムを動かすための、ラズベリーパイPico W側のプログラム・コード(スケッチ)のご紹介です。

開発環境はArduinoIDEで行っています。

今回の記事のシステムは、表示を試す意味合いが強いので、実用的な意味はありません。

また、本記事で実装コードをご紹介していますが、様々な理由により、この説明通りにいかない場合がしばしばあります。こうした場合の対応は、申し訳ありませんが、自己責任・自己解決でお進めくださるよう、お願いいたします。 

表示させるスプレッドシート


以前アップした、こちらの記事に掲載している、スマートカレンダ試作用のものを利用します。

記事の中にある、以下よりダウンロードしたひな形をスプレッドシートとしてGoogleドライブにアップし、シートをWEBページとして公開たものをそのまま使います。

公開するページは、スプレッドシートにある「ピコ」表示というシートです。

これをWEBに公開します。


公開したら、リンクを控えておいてください。


スプレッドシート側の準備は以上です。(今回GASは使いません)

ArduinoIDEでのスケッチ


次に、ラズベリーパイPico W側のプログラムを設定します。

ArduinoIDEについて


HTTPSへアクセスするプログラム事例があったことから、メジャーなMicroPythonではなく、ArduinoIDEでC言語を使ってプログラムしています。

この開発環境の初期作業については、以下に記事にしていますので、必要に応じて参照ください。

スケッチのご紹介

スケッチをご紹介します。事前にボードマネージャとライブラリ、スケッチ中の各自の設定項目を修正してからコンパイルしてください。

ボードマネージャとライブラリ

PicoWののボードマネージャは以下を使います。ArduinoIDのボードマネージャメニューから検索して追加します。

arduino-pico(earlephilhower作)

また、ライブラリは以下を使います。これらは標準で搭載されていたり、上記ボードマネージャの導入時に一緒に搭載されるものなので、個別での導入は不要です。(7セグメント表示用のライブラリは不要です)

Arduino
WiFi
HTTPClient

スケッチ

以下にスケッチのコードを掲載します。コード中の以下部分は各自で修正ください。ルート認証については、ブラウザから取得します。

WiFiの接続情報について打ち替える部分
★SSID★
★PASS★

SSL接続のためのルート認証を打ち替える部分
★各自の取得したルート認証★

ブラウザからSSL認証は取得しますが、以下の記事に取得の仕方はご紹介しました。

公開シートのURL情報情報を見て打ち替える部分
★公開ページのID★
★公開シートのID★

以上を打ち替えください。

なお、プログラムの大半は、HTTPSへのアクセスのためのSSL認証等の対応と、7セグLEDを点灯させるためのフォント情報の記載に使われています。また、稼働していることが判る様、内蔵LEDを点灯させる記述があります。


/**
作:Particlemethod:2023/11/05

0. Blink部分
  ボードマネージャArdunino OS RP2040 Boaeds をインストールください。
  これに付属のサンプルプログラム:blink を参考にしています。(下記参考)
  https://www.arduino.cc/en/Tutorial/BuiltInExamples/Blink
  ただし、下記のボードマネージャだけでも問題ないかもしれません。
  実際の書き込みは以下のマネージャです。

1.ボードマネージャ「arduino-pico(earlephilhower氏作)」を導入してください。
導入後のサンプルにある以下を参考にしています。
BasicHTTPSClient.ino
https://github.com/earlephilhower/arduino-pico/blob/master/libraries/HTTPClient/examples/BasicHttpsClient/BasicHttpsClient.ino

2.7Seg部分は以下ブログを参考にしています。iizuka氏に感謝申し上げます。
http://iizukakuromaguro.sakura.ne.jp/319_max7219/319_max7219.html

*/

//----ライブラリの導入----
#include <Arduino.h>
#include <WiFi.h>
#include <HTTPClient.h>

//----通信ピンの設定----
#define LOADpin 10 //GPIO10:チップセレクト(CS)ピン
#define DINpin  11 //GPIO11:データイン(DIN)ピン
#define CLKpin  13 //GPIO13:クロック(CLK)ピン

//----WiFi通信の設定----
#ifndef STASSID
#define STASSID "★SSID★"
#define STAPSK "★PASS★"
#endif

const char *ssid = STASSID;
const char *pass = STAPSK;

WiFiMulti WiFiMulti;


void setup() {

  //----WiFi通信の設定----

    Serial.begin(115200);
  // Serial.setDebugOutput(true);

  Serial.println();
  Serial.println();
  Serial.println();

  for (uint8_t t = 4; t > 0; t--) {
    Serial.printf("[SETUP] WAIT %d...\n", t);
    Serial.flush();
    delay(1000);
  }

  WiFi.mode(WIFI_STA);
  WiFiMulti.addAP(ssid, pass);




  // ----内蔵LEDを出力ピンに設定----
  pinMode(LED_BUILTIN, OUTPUT);

  // ----通信ピンを出力ピンに設定----
  pinMode(LOADpin,OUTPUT);
  pinMode(DINpin, OUTPUT);
  pinMode(CLKpin, OUTPUT);

  // ----通信ピンの初期設定----
  digitalWrite(LOADpin,HIGH);
  digitalWrite(DINpin, LOW );
  digitalWrite(CLKpin, LOW );

  delay(1);

  // ----7セグ試験----
  max7219write( 0x0F, 0x00, 0x0F, 0x00 ); // 表示設定 normal モード
  max7219write( 0x09, 0xFF, 0x09, 0xFF ); // モード設定 decode モード(全桁:07まで)
  max7219write( 0x0A, 0x06, 0x0A, 0x06 ); // 輝度設定 中位(0x01=dark .... 0x0F=bright)  
  max7219write( 0x0B, 0x07, 0x0B, 0x07 ); // 使用桁設定(全桁:07まで) 
  max7219write( 0x0C, 0x01, 0x0C, 0x01 ); // シャットダウン設定 normal モード

}


const char *jigsaw_cert = R"EOF(
-----BEGIN CERTIFICATE-----

★各自の取得したルート認証★

-----END CERTIFICATE-----
)EOF";

static int cnt = 0;

//ループのインターバル
int MyInt = 0;

void loop() {

  // wait for WiFi connection
  if ((WiFiMulti.run() == WL_CONNECTED)) {
    HTTPClient https;
    switch (cnt) {
      case 0:
        Serial.println("[HTTPS] using insecure SSL, not validating certificate");
        https.setInsecure(); // Note this is unsafe against MITM attacks
        cnt++;
        break;
      case 1:
        Serial.println("[HTTPS] using secure SSL, validating certificate");
        https.setCACert(jigsaw_cert);
        cnt++;
        break;
      default:
        Serial.println("[HTTPS] not setting any SSL verification settings, will fail");
        cnt = 0;
    }

    Serial.print("[HTTPS] begin...\n");


    //----------スプレッドシートのA1セル読み込み----------
    if (https.begin("https://docs.google.com/spreadsheets/d/e/★公開ページのID★/pubhtml?gid=★公開シートのID★single=true&range=A1")) {  // HTTPS

      Serial.print("[HTTPS] GET...\n");
      // start connection and send HTTP header
      int httpCode = https.GET();

      // httpCode will be negative on error
      if (httpCode > 0) {
        // HTTP header has been send and Server response header has been handled
        Serial.printf("[HTTPS] GET... code: %d\n", httpCode);

        // file found at server
        if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
          String payload = https.getString();

          //----------レスポンスの受け取り----------
          payload = payload.substring(payload.indexOf("▼▼▼▼"),payload.indexOf("●●●●"));
          Serial.println(payload.substring(12));
          payload =payload.substring(12);

          //----------レスポンスの利用----------
            //サンプル文字列  
            String stri = "=!^#";

            // LED点灯
            digitalWrite(LED_BUILTIN, HIGH);
            delay(1000); 

            // LED消灯
            digitalWrite(LED_BUILTIN, LOW); 
            delay(1000); 

            // コード指定表示テスト  上流側"01234567" 下流側"89-EHLP"
            max7219write( 0x09, 0xFF, 0x09, 0xFF ); 

            max7219write( 8, 0, 8, 8  );
            max7219write( 7, 1, 7, 9  );
            max7219write( 6, 2, 6, 10 );
            max7219write( 5, 3, 5, 11  );
            max7219write( 4, 4, 4, 12  );
            max7219write( 3, 5, 3, 13  );
            max7219write( 2, 6, 2, 14  );
            max7219write( 1, 7, 1, 15  );//スペース
            delay(5000);



            // 半非定義コード指定表示テスト  上流側"01230246" 下流側"1.2.3.4.1357"
            // 下流桁(4-7桁):非コード、上流桁(0-3桁):コード
            max7219write( 0x09, 0x0F,0x09, 0x0F );   // DIGIT 0-3 : Code   DIGIT 4-7 : No decode

            //下流桁
            max7219write( 8, 0x77, 8, Char2Code(stri.substring(0,1)));
            max7219write( 7, 0x1F, 7, Char2Code(stri.substring(1,2)) );
            max7219write( 6, 0x4E, 6, Char2Code(stri.substring(2,3)) );
            max7219write( 5, 0x80, 5, Char2Code(stri.substring(3,4)) );

            //上流桁
            max7219write( 4, 0, 4, 1  );
            max7219write( 3, 2, 3, 3  );
            max7219write( 2, 4, 2, 5 );
            max7219write( 1, 6, 1, 7  );
            delay(5000);


            // 非定義コード指定表示テスト  上流側"ABCDEFGH" 下流側"IJKLMNOP"
            
            max7219write( 0x09, 0x00,0x09, 0x00 );   // DIGIT 0-7 : No decode

            //下流桁
            max7219write( 8, Char2Code("A"), 8, Char2Code("I"));
            max7219write( 7, Char2Code("B"), 7, Char2Code("J"));
            max7219write( 6, Char2Code("C"), 6, Char2Code("K"));
            max7219write( 5, Char2Code("D"), 5, Char2Code("L"));

            //上流桁
            max7219write( 4, Char2Code("E"), 4, Char2Code("M"));
            max7219write( 3, Char2Code("F"), 3, Char2Code("N"));
            max7219write( 2, Char2Code("G"), 2, Char2Code("O"));
            max7219write( 1, Char2Code("H"), 1, Char2Code("P"));
            delay(5000);

            //--------------------------

            // 非定義コード指定表示テスト  上流側"QRSTUVWX" 下流側"レスポンスの最初8文字"

            //下流の桁
            max7219write( 8, Char2Code("Q"), 8, Char2Code(payload.substring(0,1)));
            max7219write( 7, Char2Code("R"), 7, Char2Code(payload.substring(1,2)));
            max7219write( 6, Char2Code("S"), 6, Char2Code(payload.substring(2,3)));
            max7219write( 5, Char2Code("T"), 5, Char2Code(payload.substring(3,4)));

            //上流の桁
            max7219write( 4, Char2Code("U"), 4, Char2Code(payload.substring(4,5)));
            max7219write( 3, Char2Code("V"), 3, Char2Code(payload.substring(5,6)));
            max7219write( 2, Char2Code("W"), 2, Char2Code(payload.substring(6,7)));
            max7219write( 1, Char2Code("X"), 1, Char2Code(payload.substring(7,8)));
            delay(5000);



          //----------レスポンスを受け取り損ねたら待機時間1秒、受け取ったら1時間----------
          if(payload.substring(0,1)=="D"){MyInt=60*60*1000;}else{MyInt=1000;}
        }
      } else {
        Serial.printf("[HTTPS] GET... failed, error: %s\n", https.errorToString(httpCode).c_str());
      }

      https.end();
    } else {
      Serial.printf("[HTTPS] Unable to connect\n");
    }
  }



  //==↓==↓==↓==↓==↓==↓My Code↓==↓==↓==↓====↓==↓==↓==↓
  if(MyInt==1000){
    //----------レスポンスを取れてない場合は次の試行に入る----------
    Serial.println("Wait 10s before next round_InPut...");
    delay(MyInt);
  }else{
    //----------レスポンスを取れていれば1時間待ってリブート----------
    Serial.println("Wait 1h before next round_InPut...");
    delay(MyInt);
    rp2040.reboot();

  }

}

//半角1文字を16進コードに変換
byte Char2Code(String CHR)
{
  byte Char2;

  //デフォルト
  Char2=0x00;

  //文字情報により置き換え

if(CHR=="A"){Char2=0x77;}
if(CHR=="a"){Char2=0x7D;}
if(CHR=="B"){Char2=0x1F;}
if(CHR=="C"){Char2=0x4E;}
if(CHR=="c"){Char2=0x0D;}
if(CHR=="D"){Char2=0x3D;}
if(CHR=="d"){Char2=0x3D;}
if(CHR=="E"){Char2=0x4F;}
if(CHR=="F"){Char2=0x47;}
if(CHR=="G"){Char2=0x5E;}
if(CHR=="H"){Char2=0x37;}
if(CHR=="h"){Char2=0x17;}
if(CHR=="I"){Char2=0x04;}
if(CHR=="J"){Char2=0x3C;}
if(CHR=="K"){Char2=0x57;}
if(CHR=="L"){Char2=0x0E;}
if(CHR=="l"){Char2=0x30;}
if(CHR=="M"){Char2=0x55;}
if(CHR=="N"){Char2=0x15;}
if(CHR=="O"){Char2=0x1D;}
if(CHR=="P"){Char2=0x67;}
if(CHR=="Q"){Char2=0x73;}
if(CHR=="R"){Char2=0x05;}
if(CHR=="S"){Char2=0x5B;}
if(CHR=="T"){Char2=0x0F;}
if(CHR=="U"){Char2=0x3E;}
if(CHR=="V"){Char2=0x1C;}
if(CHR=="W"){Char2=0x2A;}
if(CHR=="X"){Char2=0x13;}
if(CHR=="Y"){Char2=0x3B;}
if(CHR=="Z"){Char2=0x6C;}
if(CHR=="_"){Char2=0x08;}
if(CHR=="-"){Char2=0x01;}
if(CHR=="."){Char2=0x80;}
if(CHR==" "){Char2=0x00;}
if(CHR=="0"){Char2=0x7E;}
if(CHR=="1"){Char2=0x30;}
if(CHR=="2"){Char2=0x6D;}
if(CHR=="3"){Char2=0x79;}
if(CHR=="4"){Char2=0x33;}
if(CHR=="5"){Char2=0x5B;}
if(CHR=="6"){Char2=0x5F;}
if(CHR=="7"){Char2=0x70;}
if(CHR=="8"){Char2=0x7F;}
if(CHR=="9"){Char2=0x7B;}
if(CHR=="="){Char2=0xFE;}//0.
if(CHR=="!"){Char2=0xB0;}//1.
if(CHR=="^"){Char2=0xED;}//2.
if(CHR=="#"){Char2=0xF9;}//3.
if(CHR=="$"){Char2=0xB3;}//4.
if(CHR=="%"){Char2=0xDB;}//5.
if(CHR=="&"){Char2=0xDF;}//6.
if(CHR=="|"){Char2=0xF0;}//7.
if(CHR=="("){Char2=0xFF;}//8.
if(CHR==")"){Char2=0xFB;}//9.


  return Char2;
}

//Max7219を2連のカスケードで使う
//上流8桁(右からの桁、文字信号)、下流8桁(右からの桁、文字信号)
void max7219write( byte addrmain, byte datamain, byte addrsub, byte datasub )
{

  //チップセレクトをオン(LOW)
  digitalWrite( LOADpin, LOW );
  //下流側の桁位置を流し込む
  shiftOut( DINpin, CLKpin, MSBFIRST, addrsub );
  //下流側の文字信号を流し込む
  shiftOut( DINpin, CLKpin, MSBFIRST, datasub );
  //上流側の桁位置を流し込む
  shiftOut( DINpin, CLKpin, MSBFIRST, addrmain );
  //上流側の文字信号を流し込む
  shiftOut( DINpin, CLKpin, MSBFIRST, datamain );
  //チップセレクトをオフ(HIGH)
  digitalWrite( LOADpin, HIGH );
  delayMicroseconds(1); 
}  

プログラムの簡単な説明

スプレッドシートからの情報取得

以下のコードで、レスポンスとして、スプレッドシートからの情報を受け取っています。

//----------レスポンスの受け取り----------
payload = payload.substring(payload.indexOf("▼▼▼▼"),payload.indexOf("●●●●"));
Serial.println(payload.substring(12));
payload =payload.substring(12);


MAX7219への信号送信


max7219write()というユーザ定義関数を使っています。

詳細は上記の最後の部分をご覧下さい。

かいつまんで説明しますと、この関数max7219write()は、2連のMAX7219を使う前提の関数で、以下のいずれかの指定をします。(ここではライブラリPicoに近い側のMAX7219を上流側と呼んでいます。)

指定方法の設定:文字コードかビット列(コード非定義)のどちらを使うか送る
max7219write( 0x09、上流側の指定方法、0x09、上流側の指定方法)

表示文字の指定:文字データを文字コードまたはビット列で送る
max7219write(上流側の桁、文字データ、下流側の桁、文字データ)


この関数を使った記述がいくつかありますが、以下がキーになる部分です。
文字コードについては、これもユーザ定義関数のChar2Code()という関数で変換して得ています。

// 非定義コード指定表示テスト  上流側"QRSTUVWX" 下流側"レスポンスの最初8文字"

//下流の桁
max7219write( 8, Char2Code("Q"), 8, Char2Code(payload.substring(0,1)));
max7219write( 7, Char2Code("R"), 7, Char2Code(payload.substring(1,2)));
max7219write( 6, Char2Code("S"), 6, Char2Code(payload.substring(2,3)));
max7219write( 5, Char2Code("T"), 5, Char2Code(payload.substring(3,4)));

//上流の桁
max7219write( 4, Char2Code("U"), 4, Char2Code(payload.substring(4,5)));
max7219write( 3, Char2Code("V"), 3, Char2Code(payload.substring(5,6)));
max7219write( 2, Char2Code("W"), 2, Char2Code(payload.substring(6,7)));
max7219write( 1, Char2Code("X"), 1, Char2Code(payload.substring(7,8)));
delay(5000);

うまくスケッチが動くと、スプレッドシートに記載された文字(ヘッダ部(▼▼▼▼)よりあとの部分)の8文字と、プログラム中で指定した文字が表示されるはずです。

データ元のスプレッドシート


表示された最初の8文字(背後はプログラム中で指定した文字)

以上、スプレッドシートのデータを7セグメントLEDで表示させるための実験的な記事でした。

ライブラリPicoWを使ったギミックはまだまだ沢山考えられるのですが、このシリーズでは、スプレッドシートを日常生活に活用する、ということに注力したいと思います。

今回のトライも、スプレッドシート上のデータを、あまりお金を掛けないで、掲示できるサイズで表示させてみない、ということから行いました。

情報掲示の手段は液晶など色々ありますが、手軽にスケールアップでき、数も増やせるものとしては、フルカラーLEDと、今回の7セグLEDが適当な手段と思いますので、表示デバイスについては当面こちらに限定して記事にしようと考えています。

この記事が気に入ったらサポートをしてみませんか?