フルカラー7セグLED(WS2813-7segPanel)でお天気時計を作ろう

# はじめに
フルカラーLEDのWS2813Bを7セグLED風に並べて数字を表示する基板(WS2813-7segPanel)を作成したので、その基板を12枚連結してカレンダー時計を作成します。
フルカラーLEDの色を単純に変えて時計を作っても面白くないので、インターネットから天気予報の情報を取得して降水確率に対応した色で文字色を表現することにします。

#基板の説明
基板単体の大きさは82×100mmの大きさで、表面にはWS2813Bを24個実装して7セグ風の数字と、ピリオド(.)とコロン(:)表現できます。また右上に温度、湿度、気圧センサのBME280と、中央下側に照度センサを搭載しています。
裏面は制御用マイコンWROOM-02とその周辺回路となっています。
![単体表裏.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146211/0c578f8a-4ce9-9f74-627c-b56fced0226a.png)
基板の入手方法は[スイッチサイエンス殿](https://www.switch-science.com/catalog/7166/)で販売させてもらってます。

#完成品
![IMG_1472 (2).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146211/16298dbf-0296-6afa-e6e1-ca343eadfc4b.jpeg)
完成写真です。基板を6×2枚連結してトータル12枚で492×200mmの大きなパネルになります。
基板には上下左右に連結するためのはんだパターンがあるので半田で連結して作成します。
上の段は「月日」、「温度・湿度」、「気圧」を3秒間隔で切替て表示します。
下の段は時間を表示します。せっかくフルカラーLEDを使っているので時間の文字色は天気予報の降水確率を色に変換して表示することにします。

#パネル作成#
制御マイコンを搭載した基板1枚と、マイコン無しの基板11枚を使ってパネルを作成します。
裏面からみて左下にマイコン搭載基板を置いて、信号が青の矢印方法に流れるようにマイコン無し基板を並べて、緑の部分と赤のジャンバーを半田でショートさせて連結します。また、上下の基板を固定するためGNDパターンも半田でショートします。
基板連結は半田のみでは無理なので、リード線としてピンヘッダのピンを抜いて使います。硬さ長さともちょうどいい感じです。
![6連結ジャンパ表示.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146211/ac88b2f5-a8ed-e9f3-cb85-43e98442f688.jpeg)

#降水確率取得の準備#
マイコンで天気予報を取得できるサイトをいろいろ探しましたが、日本のサイトでは見つからなかったので海外のサイトから取得することにしました。
無料で5日先までの天気予報を取得できるAPIを提供してくれている[OpenWeather](https://openweathermap.org/)を使うことにします。
このサービスを使うためには[OpenWeather](https://openweathermap.org/)にアカウントを作成する必要があります。
Create New Account画面で必要な情報を入力してアカウントを作成します。
アカウントが作成出来たらアカウント名の右の▼から[My API Keys]を選択してAPIキーを取得します。
実際に天気予報が取得できるか試してみます。場所はlatに緯度、lonに経度を設定して、appidに取得したAPIキーに置き換えてブラウザーのアドレスに入力します。
https://api.openweathermap.org/data/2.5/onecall?lat=34.68750348806358&lon=135.5259251795476&exclude=current&appid=**************************&units=metric%22
以下のような表示になれば問題ありません。
{"lat":34.6875,"lon":135.5259,"timezone":"Asia/Tokyo","timezone_offset":32400,"minutely":
・・・

次にhttpsサイトに接続する場合に必要になるフィンガープリントを取得します。
[OpenWeather](https://openweathermap.org/)をブラウザーで表示した状態で以下の手順でフィンガープリントを確認します。
[Chrome]の場合
1．Developer Tools を開く
2．Security タブを開く
3．View Certificate ボタン押下
4．詳細タブを選択して拇印を選択すると下に16進の数字40桁が表示される
この数字がフィンガープリントでプログラム作成時に必要になります。

これで天気予報をプログラムから取得する準備は完了です。

#プログラム
全ソースを記載します。
ソース内の[APDKEY]、[LAT]、[LON]は自分のAPIキー、緯度、経度に置き換えます。
緯度、経度はGoogleMapで簡単に取得できます。
またfingerprint[20]の配列にフィンガープリントを2桁づつ区切った値に置き換えます。

```C++:RainClockVer2.ino
/********************************************/
//　NTPサーバから時刻を取得する時計
//　パネル12枚を使って、時分秒を表示
//　OpenWeatherのAPIで降水確率をとり、文字色を変える
//
//  2021.4.03  Ver　2.0.0  初期　
//  2021.4.20  Ver　2.0.1  予報の日数を3，6日切替機能追加　
/********************************************/

#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266mDNS.h>
#include <WiFiUdp.h>
#include <WiFiClientSecure.h>
#include <ESP8266HTTPClient.h>
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h> //https://github.com/tzapu/WiFiManager
#include <EEPROM.h>

#include <TimeLib.h>
#include <Time.h>

#include "WS2813Panel.h"
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

//-----------
#define VER 201
#define PANEL_NUM 12
#define JST 3600 * 9
#define TRIGGER_PIN 0

const String HOST = "https://api.openweathermap.org/data/2.5/onecall";
const String APIKEY = "***********************";
const String LAT = "xx.xxxx";	//緯度
const String LON = "xxx.xxxx";	//経度

//-----------
//色
const uint32_t GREEN = 0xFF0000;
const uint32_t RED = 0x00FF00;
const uint32_t BULE = 0x0000FF;
const uint32_t YELLOW = 0xFFFF00;
const uint32_t CYAN = 0xFF00FF;
const uint32_t MAGENTA = 0x00FFFF;
const uint32_t WHITE = 0xFFFFFF;

const uint8_t fingerprint[20] = {0xXX, 0xXX, 0xXX, 0xXX, 0xXXf, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX, 0xXX};
static const char *wd[7] = {"Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat"};

uint32_t COLOR[3] = {0xff0000, 0x00ff00, 0x0000ff};

Adafruit_BME280 bme; // I2C

//-----------
WS2813Panel MyPanel(PANEL_NUM);
uint8_t bright = 50;
uint32_t swstat = 0;

float RainRates[8] = {999}; //8日分の降水確率
uint8_t lights[64] = {};

bool DEMO = false;
int Mode = -1;           //月、温湿度、気圧　表示切替
uint8_t ForecastDay = 0; //0=3dyas 1=6days

int ChangeTime = 3000; //表示切替時間(msec)
uint NextGetTime;

float Temp;
float Humidity;
float preasure;

//-----------
void setup()
{
  Serial.begin(115200);
  Serial.println("Program Start");

  Serial.println("LED Initialze");
  MyPanel.Begin();
  delay(10);
  MyPanel.Clear();
  MyPanel.Show();
  //照度自動調整ON
  MyPanel.AutoBright = true;
  pinMode(TRIGGER_PIN, INPUT);

  //WiFiManager
  //Local intialization. Once its business is done, there is no need to keep it around
  WiFiManager wifiManager;
  wifiManager.autoConnect("AutoConnectAP");
  while (WiFi.status() != WL_CONNECTED)
  {
    Serial.print(".");
    delay(500);
  }

  Serial.println("connected");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  //NTP
  configTzTime("JST-9", "ntp.nict.jp", "ntp.jst.mfeed.ad.jp"); // 2.7.0以降, esp32コンパチ
  delay(1000);

  time_t t = time(NULL);
  struct tm *tlocal = localtime(&t);
#if 1
  Serial.printf(" %04d/%02d/%02d(%s) %02d:%02d:%02d\n",
                tlocal->tm_year + 1900, tlocal->tm_mon + 1, tlocal->tm_mday,
                wd[tlocal->tm_wday],
                tlocal->tm_hour, tlocal->tm_min, tlocal->tm_sec);
#endif
  //BME280初期化　I2c SDA=4　SDC=5　ピン
  Wire.begin(4, 5);
  unsigned status = bme.begin(0x76);
  if (!status)
  {
    Serial.println("Could not find a valid BME280 sensor, check wiring, address, sensor ID!");
    Serial.print("SensorID was: 0x");
    Serial.println(bme.sensorID(), 16);
    while (1)
      delay(10);
  }
  //Ver表示
  DispVer();

  //予報取得日数
  GetForecastDay();

  //次回画面切替タイム設定
  NextGetTime = millis() + ChangeTime;

  //降水確率
  GetRainFallOpenWeather();
}
//-------------------------------------------------
void DispVer()
{
  MyPanel.DispNum(2, (VER / 100), 0xffffff);
  MyPanel.DispDot(2, 0xffffff);
  MyPanel.DispNum(1, ((VER / 10) %10), 0xffffff);
  MyPanel.DispDot(1, 0xffffff);
  MyPanel.DispNum(0, (VER % 10), 0xffffff);
  MyPanel.Show();
  delay(2000);
  MyPanel.Clear();
  MyPanel.Show();
}
//-------------------------------------------------
uint8_t GetForecastDay()
{
  EEPROM.begin(16);
  EEPROM.get<uint8_t>(0, ForecastDay);
  Serial.print("ForecastDay : ");
  Serial.println(ForecastDay);
  if (ForecastDay == 0)
    MyPanel.DispNum(1, 3, 0x0000ff);
  else
    MyPanel.DispNum(1, 6, 0x0000ff);
  MyPanel.DispNum(0, 0x1a, 0x0000ff);//d
  
  MyPanel.Show();
}
//-------------------------------------------------
void loop()
{
  static int presec;
  static int startsec;
  static uint16_t cnt = 0;
  uint32_t colors[6] = {};

  time_t t = time(NULL);
  struct tm *tlocal = localtime(&t);
#if 0
  Serial.printf(" %04d/%02d/%02d(%s) %02d:%02d:%02d\n",
                tlocal->tm_year + 1900, tlocal->tm_mon + 1, tlocal->tm_mday,
                wd[tlocal->tm_wday],
                tlocal->tm_hour, tlocal->tm_min, tlocal->tm_sec);
#endif

  //スイッチ監視(チャタリング防止)
  if (digitalRead(TRIGGER_PIN) == LOW)
  {
    swstat = (swstat << 1) | 0x1;
    Serial.println(swstat);
    //4秒長押しでデモモード
    if (swstat == 0xFFFFFFFF)
    {
      DEMO = !DEMO;
      swstat = 0;
      MyPanel.Clear();
      MyPanel.Show();
    }
    //予報日数切替
    if (swstat == 0x0F)
    {
      ForecastDay = (ForecastDay == 0) ? 1 : 0;
      EEPROM.put<byte>(0, ForecastDay);
      EEPROM.commit();
      if (ForecastDay == 0)
        MyPanel.DispNum(0, 3, 0xffffff);
      else
        MyPanel.DispNum(0, 6, 0xffffff);
      MyPanel.Show();
    }
    delay(100);
    return;
  }
  else
  {
    swstat = 0;
  }

  presec = tlocal->tm_sec;

  //毎時1分、31分に降水確率取得
  if (((tlocal->tm_min % 30) == 1 && tlocal->tm_sec == 0))
  {
    GetRainFallOpenWeather();
    Serial.println("GetRainFallOpenWeather");
  }

  //DEMOモード 降水確率ランダムに変更
  if (DEMO)
  {
    randomSeed(tlocal->tm_sec);
    RainRates[0] = random(0, 100);
    RainRates[1] = random(0, 100);
    RainRates[2] = random(0, 100);
  }

  for (int i = 0; i < 6; i++)
  {
    byte R;
    byte G;
    byte B;
    if (RainRates[i] > 100.0) //取得失敗
    {
      R = 128.0;
      B = 0;
      G = 128.0;
    }
    else if (RainRates[i] > 50)
    {
      R = (255.0 * (RainRates[i] - 50.0) / 50.0);
      B = (255.0 * (100.0 - RainRates[i]) / 50.0);
      G = 0;
    }
    else
    {
      B = (255.0 * RainRates[i] / 50.0);
      G = (255.0 * (50.0 - RainRates[i]) / 50.0);
      R = 0;
    }
    //    Serial.printf("rain_max[%d] = %d B = %d G = %d R = %d\n", i, rain_max[i], B, G, R);
    colors[i] = B | (G << 8) | (R << 16);

#if 0 //
    colors[i] = Wheel((byte)((cnt / 10) % 0xff));
#endif
  }
  if (ForecastDay == 0) //3日モードの時は、2桁ずつ同じ数字にする
  {
    colors[5] = colors[2];
    colors[4] = colors[2];
    colors[3] = colors[1];
    colors[2] = colors[1];
    colors[1] = colors[0];
  }

  //指定時間ごととに表示モードを更新
  if (millis() > NextGetTime)
  {
    Mode = (Mode + 1) % 3;
    NextGetTime = millis() + ChangeTime;
    Temp = bme.readTemperature();
    Humidity = bme.readHumidity();
    preasure = bme.readPressure() / 100.0F;

    //DEMOモード 温度湿度をランダムに変更
    if (DEMO)
    {
      Temp = random(-9, 50);
      Humidity = random(1, 99);
    }
  }

  switch (Mode)
  {
  case 0: //月日表示
    DispDate(tlocal, colors[0]);
    break;
  //    case 1://温度
  //      DispTemp(Temp); break;
  //    case 2://湿度
  //      DispHumid(Humidity); break;
  case 1:
    DispTempHumid(Temp, Humidity);
    break;
  case 2: //気圧
    DispPress(preasure);
    break;
  }

  //時分秒表示
  DispTime(tlocal, colors);
}
//-------------------------------------------------
void GetRainFallOpenWeather()
{
  char *key[] = {"daily", "dt\":", "pop\":"};

  String host1 = HOST;
  host1 += "?lat=" + LAT;
  host1 += "&lon=" + LON;
  host1 += "&exclude=current&units=metric";
  host1 += "&appid=" + APIKEY;
  Serial.println(host1);

  std::unique_ptr<BearSSL::WiFiClientSecure> client(new BearSSL::WiFiClientSecure);
  client->setFingerprint(fingerprint);

  HTTPClient https;
  char buff[16] = {0};
  String temp;
  char c;
  int searchidx = 0; //検索キー
  int pos = 0;
  int dayindex = 0;
  char *pbuff;

  Serial.print("[HTTPS] begin...\n");
  // HTTPS
  if (https.begin(*client, host1))
  {

    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)
      {
        //取得データ長
        int len = https.getSize();
        Serial.printf("getSize() = %d\n", len);

        WiFiClient *stream = https.getStreamPtr();
        // get available data size
        int strmsize = (int)(stream->available());
        Serial.printf("      size = %d  len = %d\n", strmsize, len);
        if (strmsize == 0)
        {
          Serial.println("************* break *****************");
          https.end();
          return;
        }
        bool findflg = false;

        while (len > 0)
        {
          stream->readBytes(&c, 1);
          //検索文字に一致するか
          if (key[searchidx][pos] == c)
          {
            if (pos == (strlen(key[searchidx]) - 1))
            {
              if (searchidx == 0) //"daily"検索
              {
                Serial.printf("Find %s\n", key[searchidx]);
                searchidx = 1; //検索文字を"dt\"
              }
              else if (searchidx == 1)
              {
                Serial.printf("Find %s\n", key[searchidx]);
                pbuff = &buff[0];
                while (1)
                {
                  stream->readBytes(&c, 1);
                  if (c == ',')
                  {
                    *pbuff = 0;
                    searchidx = 2;
                    time_t ttt = atoi(buff);
                    struct tm *tttlocal = localtime(&ttt);

                    Serial.printf(" %04d/%02d/%02d",
                                  tttlocal->tm_year + 1900, tttlocal->tm_mon + 1, tttlocal->tm_mday);
                    break;
                  }
                  //Serial.print(c);
                  *pbuff = c;
                  pbuff++;
                }
              }
              else if (searchidx == 2) //"pop"が見つかった
              {
                //                Serial.printf("Find %s\n", key[searchidx]);
                pbuff = &buff[0];
                while (1)
                {
                  stream->readBytes(&c, 1);
                  if (c == ',')
                  {
                    *pbuff = 0;
                    searchidx = 1;
                    float rate = atof(buff);
                    Serial.printf("  %3.0f%%\n", round(rate * 100.0));
                    RainRates[dayindex++] = round(rate * 100.0);
                    break;
                  }
                  //Serial.print(c);
                  *pbuff = c;
                  pbuff++;
                }
                if (dayindex > 6)
                  break;
              }
            }
            pos++;
          }
          else
          {
            pos = 0;
          }
          len--;
        }
        https.end();
        Serial.printf("https.end()\n");
      }
      else
      {
        Serial.printf("[HTTPS] Unable to connect\n");
      }
    }
  }
}

//------------------------------------
//月日を表示
//------------------------------------
void DispDate(struct tm *tm, uint32_t clr)
{
  int8_t mnum[2];                  //月の数字
  mnum[1] = (tm->tm_mon + 1) / 10; //10の位
  mnum[0] = (tm->tm_mon + 1) % 10; //1の位

  int8_t dnum[2]; //日の数字
  dnum[1] = tm->tm_mday / 10;
  dnum[0] = tm->tm_mday % 10;

  for (int i = 6; i < 12; i++)
    MyPanel.DispNum(i, 0x10, 0); //空白

  MyPanel.DispNum(8, 0x11, clr); //月と日の間に「-」
  //月
  for (int i = 0; i < 2; i++)
  {
    if (i == 1 && mnum[i] == 0)
      MyPanel.DispNum(7 - i, 0x10, clr); //空白
    else
      MyPanel.DispNum(7 - i, mnum[i], clr);
  }
  //日
  for (int i = 0; i < 2; i++)
  {
    if (i == 1 && dnum[i] == 0)
      MyPanel.DispNum(10 - i, 0x10, clr);
    else
      MyPanel.DispNum(10 - i, dnum[i], clr);
  }

  MyPanel.Show();
}
//------------------------------------
//時間表示 パネルごとに色を設定
//------------------------------------
void DispTime(struct tm *tm, uint32_t clr[])
{
  static int presec;
  static int startsec;

  if (presec != tm->tm_sec)
  {
    startsec = millis();
  }
  presec = tm->tm_sec;

  int8_t hnum[2];
  int8_t mnum[2];
  int8_t snum[2];

  hnum[1] = tm->tm_hour / 10; //10の位
  hnum[0] = tm->tm_hour % 10; //1の位
  mnum[1] = tm->tm_min / 10;
  mnum[0] = tm->tm_min % 10;
  snum[1] = tm->tm_sec / 10;
  snum[0] = tm->tm_sec % 10;

  //  Serial.printf("%d %d %d %d %d %d \n", hnum[1], hnum[0], mnum[1], mnum[0], snum[1], snum[0]);

  for (int i = 0; i < 2; i++)
  {
    if (i == 1 && hnum[i] == 0) //10の位が0は表示しない
      MyPanel.DispNum(i + 4, 0x10, 0);
    else
      MyPanel.DispNum(i + 4, hnum[i], clr[4 + i]);
  }
  for (int i = 0; i < 2; i++)
  {
    MyPanel.DispNum(i + 2, mnum[i], clr[2 + i]);
  }
  for (int i = 0; i < 2; i++)
  {
    MyPanel.DispNum(i, snum[i], clr[i]);
  }

  //時計のコロンを0.5sec毎に点滅
  if (((float)millis() - (float)startsec) < 500)
  {
    MyPanel.DispColon(2, clr[2]);
    MyPanel.DispColon(4, clr[4]);
  }
  else
  {
    MyPanel.DispColon(2, 0);
    MyPanel.DispColon(4, 0);
  }
  MyPanel.Show();
  delay(10);
}
//------------------------------------
//時間表示 全パネル同じ色
//------------------------------------
void DispTime(struct tm *tm, uint32_t clr)
{
  uint32_t clrs[6];
  for (int i = 0; i < sizeof(clrs) / sizeof(clrs[0]); i++)
    clrs[i] = clr;
  DispTime(tm, clrs);
}
//--------------------------------
//　気温・湿度表示
//--------------------------------
void DispTempHumid(float t, float h)
{
  bool tempsign = (t >= 0); //符号
  uint8_t dec_temp[6];      // [5][4]が温度[3]は'℃' [2][1]は湿度　[0]は%
  dec_temp[3] = 20;         //℃
  dec_temp[0] = 21;         //%

  //温度
  int temp = abs(round(t));

  dec_temp[5] = temp / 10;
  dec_temp[4] = temp % 10;
  if (tempsign == false) //温度マイナス
    dec_temp[5] = 0x11;
  else if (dec_temp[5] == 0)
    dec_temp[5] = 0x10;

  //湿度
  int humd = abs(round(h));

  dec_temp[2] = humd / 10;
  dec_temp[1] = humd % 10;

  if (dec_temp[2] == 0)
    dec_temp[2] = 0x10;

  //不快指数
  float thi = GetTempHumidDHT(t, h);
  uint32_t clr = ConvertColorTHI(thi);
  for (int i = 6; i < 12; i++)
    MyPanel.DispNum(i, 0x10, 0); //空白

  for (int pnl = 0; pnl < 6; pnl++)
  {
    MyPanel.DispNum(6 + pnl, dec_temp[5 - pnl], clr);
  }
  MyPanel.Show();
}

//--------------------------------
//　気温表示
//--------------------------------
void DispTemp(float t)
{
  uint8_t dec_temp[4]; // [3][2].[1] [0]℃  -の場合は[3]が「-」で-9.9℃まで
  dec_temp[0] = 20;    //℃
  //温度を10倍
  int tempx10 = int(abs(t * 10.0));

  for (int i = 1; i < 4; i++)
  {
    dec_temp[i] = tempx10 % 10;
    tempx10 = tempx10 / 10;
  }

  //マイナス
  if (t < 0)
  {
    dec_temp[3] = 0x11;
  }
  else
  {
    if (abs(t) < 10)
      dec_temp[3] = 0x10;
  }

  //不快指数
  float thi = GetTempHumidDHT(Temp, Humidity);
  uint32_t clr = ConvertColorTHI(thi);
  for (int i = 6; i < 12; i++)
    MyPanel.DispNum(i, 0x10, 0); //空白

  for (int pnl = 0; pnl < 4; pnl++)
  {
    MyPanel.DispNum(10 - pnl, dec_temp[pnl], clr);
  }
  //パネル2枚目に小数点
  MyPanel.DispDot(10 - 2, clr);
  MyPanel.Show();
}

//------------------------------------
//　湿度表示
//------------------------------------
void DispHumid(float h)
{
  uint8_t dec_temp[4];
  dec_temp[0] = 21; //%
  //湿度を10倍
  int tempx10 = int(h * 10.0);

  for (int i = 1; i < 4; i++)
  {
    dec_temp[i] = tempx10 % 10;
    tempx10 = tempx10 / 10;
  }

  //不快指数
  float thi = GetTempHumidDHT(Temp, Humidity);
  uint32_t clr = ConvertColorTHI(thi);
  for (int i = 6; i < 12; i++)
    MyPanel.DispNum(i, 0x10, 0); //空白

  for (int pnl = 0; pnl < 4; pnl++)
  {
    if (pnl == 3 && dec_temp[pnl] == 0)
      MyPanel.SetPanelColor(10 - pnl, 0);
    else
      MyPanel.DispNum(10 - pnl, dec_temp[pnl], clr);
  }
  //パネル2枚目に小数点
  MyPanel.DispDot(10 - 2, clr);
  MyPanel.Show();
}
//------------------------------------
//気圧表示
//------------------------------------
void DispPress(float p)
{
  uint8_t dec_temp[6];
  dec_temp[0] = 25; //P
  dec_temp[1] = 22; //h

  int tempx10 = p;

  for (int i = 2; i < 6; i++)
  {
    dec_temp[i] = tempx10 % 10;
    tempx10 = tempx10 / 10;
  }
  for (int i = 6; i < 12; i++)
    MyPanel.DispNum(i, 0x10, 0); //空白

  if (dec_temp[5] == 0)
    dec_temp[5] = 0x10;
  for (int pnl = 0; pnl < 6; pnl++)
  {
    MyPanel.DispNum(11 - pnl, dec_temp[pnl], 0xdfdfdf);
  }
  MyPanel.Show();
}
//-------------------------------------------------
//不快指数
//-------------------------------------------------
float GetTempHumidDHT(float t, float h)
{
  float THI = 0.81 * t + 0.01 * h * (0.99 * t - 14.3) + 46.3;
#if 0
  Serial.print("THI = ");
  Serial.print(THI);
  Serial.println();
#endif
  return THI;
}

//-------------------------------------------------
//不快指数を色にする、0(青)-50(緑)-100(赤)　寒い0　暑い100
//-------------------------------------------------
uint32_t ConvertColorTHI(float thi)
{
  uint32_t LEDColor = 0;
  byte R;
  byte G;
  byte B;
  if (thi > 85)
  {
    R = 0xff;
    B = 0;
    G = 0;
  }
  else if (thi < 55)
  {
    R = 0;
    B = 0xff;
    G = 0;
  }
  else if (thi > 67.5)
  {
    R = (thi - 67.5) / (85.0 - 67.5) * 0xff;
    G = 0xff - R;
    B = 0;
  }
  else if (thi <= 67.5)
  {
    B = (thi - 67.5) / (55.0 - 67.5) * 0xff;
    G = 0xff - B;
    R = 0;
  }
  //  Serial.printf("%2X %2X %2X\n", R, G, B);

  LEDColor = (R << 16) + (G << 8) + B;
#if 0
  Serial.printf("%2X %2X %2X %8X\n", R, G, B, LEDColor);
#endif
  return LEDColor;
}

```

###プログラム書き込み方法
1.制御基板のシリアルポート(J3)にＵＳＢシリアル変換モジュールを接続します。モジュール側のTXは基板側のRX、ＲＸはＴＸ、ＧＮＤはＧＮＤになるよう接続してください。
2.基板のUSB-Cコネクタから5Vを供給します。
3.基板上側のWriteONのスイッチを押したまま、RSTスイッチを押して離します。その後WriteONスイッチを離します。
4.ArduinoIDEのボードの設定、シリアルポートの設定をして[マイコンボートに書き込む]をクリックします。しばらくして完了のメッセージが表示されれば書き込み完了です。
5.RSTボタンを押すとプログラムが実行されます。
 

#基板回路図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146211/df28ab59-6668-3e6e-d1d4-85ab27bf7f52.png)

#参考リンク
[GitHub WS2813-7segPanelライブラリー](https://github.com/VEC01773/WS2813-7segPanel)
[YouTube RainClock2](https://youtu.be/SeoeUBH3Rpc)
[WifiManagerの使い方](https://takehikoshimojima.tumblr.com/post/138820924644/esp8266-wi-fi-ssid%E3%83%91%E3%82%B9%E3%83%AF%E3%83%BC%E3%83%89%E3%81%AE%E3%82%AA%E3%83%B3%E3%83%A9%E3%82%A4%E3%83%B3%E8%A8%AD%E5%AE%9A)
[コントローラ付基板](https://www.switch-science.com/catalog/7165/)
[コントローラ無し基板](https://www.switch-science.com/catalog/7166/)