ESP8266をWifiモデムとして使う

照度センサー付きアナログ電波時計


前回、AT-FirmwareのSNTP機能を利用したアナログ 時計を紹介しました。
まだ、D12とD13のピンが余っています。
そこで、D13ピンに照度センサーを付けて、暗くなったらTFTを消すようにしてみました。
照度センサーはアナログデバイスなので、コンパレータでディジタル値に変換しています。
使用したコンパレータはLM393です。
5Vを100:470に分圧し、4.1Vを基準電圧としています。
これでD13の値だけで明暗が判断できるようになります。


さらにD12ピンには、こちらで紹介しているピン ポンチャイムを付けて時刻合わせの時にチャイムを鳴らしています。
スケッチは以下の通りで、前回から大幅に変更しました。
CHIMEの値が0の時はピンポンチャイムが無効に、LM393の値が0の時は照度センサーが無効になります。
/*
 * Analog Radio Clock with AT Command
 * 
 * ESP8266----------ATmega
 * TX     ----------RX(D10)
 * RX     ----------TX(D11)
 *
 */

#include <SoftwareSerial.h>
#include <TimeLib.h>     // https://github.com/PaulStoffregen/Time

#define rxPin    10    // D10
#define txPin    11    // D11
#define CHIME    12    // D12 0=Disable Chime
#define LM393    13    // D13 0=Disable CDS

SoftwareSerial Serial2(rxPin, txPin); // RX, TX

#include <avr/wdt.h>
#include "Adafruit_GFX.h"// Hardware-specific library
#include <MCUFRIEND_kbv.h>  // https://github.com/prenticedavid/MCUFRIEND_kbv

MCUFRIEND_kbv tft;

// Color definitions
#define BLACK       0x0000      /*   0,   0,   0 */
#define NAVY        0x000F      /*   0,   0, 128 */
#define DARKGREEN   0x03E0      /*   0, 128,   0 */
#define DARKCYAN    0x03EF      /*   0, 128, 128 */
#define MAROON      0x7800      /* 128,   0,   0 */
#define PURPLE      0x780F      /* 128,   0, 128 */
#define OLIVE       0x7BE0      /* 128, 128,   0 */
#define LIGHTGRAY   0xC618      /* 192, 192, 192 */
#define DARKGRAY    0x7BEF      /* 128, 128, 128 */
#define BLUE        0x001F      /*   0,   0, 255 */
#define GREEN       0x07E0      /*   0, 255,   0 */
#define CYAN        0x07FF      /*   0, 255, 255 */
#define RED         0xF800      /* 255,   0,   0 */
#define MAGENTA     0xF81F      /* 255,   0, 255 */
#define YELLOW      0xFFE0      /* 255, 255,   0 */
#define WHITE       0xFFFF      /* 255, 255, 255 */
#define ORANGE      0xFD20      /* 255, 165,   0 */
#define GREENYELLOW 0xAFE5      /* 173, 255,  47 */
#define PINK        0xF81F
#define GRAY        0x5AEB

#define TIME_ZONE   9 // Change your time zone
#define _DEBUG_     0

float sx = 0, sy = 1, mx = 1, my = 0, hx = -1, hy = 0;    // Saved H, M, S x & y multipliers
float sdeg=0, mdeg=0, hdeg=0;
uint16_t osx=120, osy=120, omx=120, omy=120, ohx=120, ohy=120;  // Saved H, M, S x & y coords
uint16_t x00=0, x11=0, y00=0, y11=0;
uint32_t targetTime = 0;                    // for next 1 second timeout
boolean initial = 1;
time_t epoch;
char buf[64];

#if 0
void reboot() {
  wdt_disable();
  wdt_enable(WDTO_15MS);
  while (1) {}
}
#endif

void putChar(char c) {
  char tmp[10];
  if ( c == 0x0a) {
    Serial.println();
  } else if (c == 0x0d) {
   
  } else if ( c < 0x20) {
    uint8_t cc = c;
    sprintf(tmp,"[0x%.2X]",cc);
    Serial.print(tmp);
  } else {
    Serial.print(c);
  }
}

//Wait for specific input string until timeout runs out
bool waitForString(char* input, int length, unsigned int timeout) {
  unsigned long end_time = millis() + timeout;
  char current_byte = 0;
  int index = 0;

   while (end_time >= millis()) {
   
      if(Serial2.available()) {
       
        //Read one byte from serial port
        current_byte = Serial2.read();
//        Serial.print(current_byte);
        if (_DEBUG_) putChar(current_byte);
        if (current_byte != -1) {
          //Search one character at a time
          if (current_byte == input[index]) {
            index++;
           
            //Found the string
            if (index == length) {             
              return true;
            }
          //Restart position of character to look for
          } else {
            index = 0;
          }
        }
      }
  } 
  //Timed out
  return false;
}

//Wait for CIPSNTPTIME response
int getSNTPtime(char* buf, int szbuf, unsigned int timeout) {
  int len=0;
  int pos=0;
  char line[40];

  Serial2.print("AT+CIPSNTPTIME?\r\n");
  long int time = millis();

  memset(line,0,sizeof(line));
  while( (time+timeout) > millis()) {
    while(Serial2.available())  {
      char c = Serial2.read(); // read the next character.
      if (_DEBUG_) Serial.print(c);
      if (c == 0x0d) {
         
      } else if (c == 0x0a) {
        if (_DEBUG_) {
          Serial.print("Read=[");
          Serial.print(line);
          Serial.println("]");
        }
        if (strcmp(line,"OK") == 0) return len;
        int offset;
        for(offset=0;offset<pos;offset++) {
          if(line[offset] == '+') break;
        }
        if (strncmp(&line[offset],"+CIPSNTPTIME:",13) == 0) {
          strcpy(buf,&line[13+offset]);
          len = strlen(buf);
          buf[len] = 0;
        }
        pos=0;
        line[pos]=0;
      } else {
        line[pos++]=c;
        if (pos == 40) return -1;
        line[pos]=0;
      }
    } 
  }
  return -1;
}

//うるう年の判定
int LeapYear(int yyyy) {
  if (yyyy % 4 == 0) {
    if (yyyy % 100 == 0) {
      if (yyyy % 400 == 0) {
        //Serial.println("LeapYear");
        return 1;
      } else {
        //Serial.println("NOT LeapYear");
        return 0;
      }
    } else {
      //Serial.println("LeapYear");
      return 1;
    }
  } else {
    //Serial.println("NOT LeapYear");
    return 0;
  }
}

//#define LEAP_YEAR(Y)     ( ((1970+Y)>0) && !((1970+Y)%4) && ( ((1970+Y)%100) || !((1970+Y)%400) ) )
#define SECS_PER_MIN  ((time_t)(60UL))
#define SECS_PER_HOUR ((time_t)(3600UL))
#define SECS_PER_DAY  ((time_t)(SECS_PER_HOUR * 24UL))
#define DAYS_PER_WEEK ((time_t)(7UL))
#define SECS_PER_WEEK ((time_t)(SECS_PER_DAY * DAYS_PER_WEEK))
#define SECS_PER_YEAR ((time_t)(SECS_PER_DAY * 365UL)) // TODO: ought to handle leap years
#define SECS_YR_2000  ((time_t)(946684800UL)) // the time at the start of y2k

// Convert Sat Jan 20 08:41:55 2018 to time_t
time_t makeTime(char* str) {
  char c_dow[10];
  char c_month[10];
  int year,month,day,hour,minute,second,dow;
  char *t_dow[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
  char *t_month[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};

  sscanf(str,"%s %s %d %d:%d:%d %d",c_dow,c_month,&day,&hour,&minute,&second,&year);

  for(dow=0;dow<7;dow++) {
    if (strcmp(c_dow,t_dow[dow]) == 0) break;
  }

  for(month=1;month<13;month++) {
    if (strcmp(c_month,t_month[month-1]) == 0) break;
  }

  // API starts months from 1, this array starts from 0
  static  const uint8_t monthDays[]={31,28,31,30,31,30,31,31,30,31,30,31};

  time_t seconds;
  int i;

  // seconds from 1970 till 1 jan 00:00:00 of the given year
  seconds= (year-1970)*(SECS_PER_DAY * 365);
  for (i = 1970; i < year; i++) {
    if (LeapYear(i)) {
      seconds +=  SECS_PER_DAY;   // add extra days for leap years
     }
  }

  // add days for this year, months start from 1
  for (i = 1; i < month; i++) {
    if ( (i == 2) && LeapYear(year)) {
      seconds += SECS_PER_DAY * 29;
    } else {
      seconds += SECS_PER_DAY * monthDays[i-1];  //monthDay array starts from 0
    }
  }
  seconds+= (day-1) * SECS_PER_DAY;
  seconds+= hour * SECS_PER_HOUR;
  seconds+= minute * SECS_PER_MIN;
  seconds+= second;

  return seconds;
}

//Send AT Command
void sendCommand(char* buff) {
  if (_DEBUG_) {
    Serial.println("");
    Serial.print(buff);
    Serial.println("-->");
  }
  Serial2.println(buff);
  Serial2.flush();
}

//Print error
void errorDisplay(char* buff) {
  Serial.print("Error:");
  Serial.println(buff);
  while(1) {}
}


void clearBuffer() {
  while (Serial2.available())
    Serial2.read();
//  Serial.println();
}

static uint8_t conv2d(const char* p) {
  uint8_t v = 0;
  if ('0' <= *p && *p <= '9')
    v = *p - '0';
  return 10 * v + *++p - '0';
}

void drawWatch(char * dateStr) {
  uint16_t frame = ORANGE;
  uint16_t wid = tft.width();
  uint16_t ht = tft.height();
  if (isPM()) frame = GREEN;
  if (wid < ht) {
    tft.setRotation(0);
  } else {
    tft.setRotation(1);
  }
  tft.fillScreen(DARKGRAY);
  tft.setTextColor(WHITE, DARKGRAY);  // Adding a background colour erases previous text automatically
 
  // Draw clock face
  tft.fillCircle(120, 120, 118, frame);
  tft.fillCircle(120, 120, 110, BLACK);

  // Draw 12 lines
  for(int i = 0; i<360; i+= 30) {
    sx = cos((i-90)*0.0174532925);
    sy = sin((i-90)*0.0174532925);
    x00 = sx*114+120;
    y00 = sy*114+120;
    x11 = sx*100+120;
    y11 = sy*100+120;

    tft.drawLine(x00, y00, x11, y11, GREEN);
  }

  // Draw 60 dots
  for(int i = 0; i<360; i+= 6) {
    sx = cos((i-90)*0.0174532925);
    sy = sin((i-90)*0.0174532925);
    x00 = sx*102+120;
    y00 = sy*102+120;
    // Draw minute markers
    tft.drawPixel(x00, y00, WHITE);
   
    // Draw main quadrant dots
    if(i==0 || i==180) tft.fillCircle(x00, y00, 2, WHITE);
    if(i==90 || i==270) tft.fillCircle(x00, y00, 2, WHITE);
  }

  tft.fillCircle(120, 121, 3, WHITE);
//  tft.setCursor(20, 260);
  tft.setCursor(30, 260);
  tft.setTextSize(3);
  tft.println(dateStr);
 
}

void drawTime(uint8_t hh, uint8_t mm, uint8_t ss) {
    // Pre-compute hand degrees, x & y coords for a fast screen update
    sdeg = ss*6;                  // 0-59 -> 0-354
    mdeg = mm*6+sdeg*0.01666667;  // 0-59 -> 0-360 - includes seconds
    hdeg = hh*30+mdeg*0.0833333;  // 0-11 -> 0-360 - includes minutes and seconds
    hx = cos((hdeg-90)*0.0174532925);   
    hy = sin((hdeg-90)*0.0174532925);
    mx = cos((mdeg-90)*0.0174532925);   
    my = sin((mdeg-90)*0.0174532925);
    sx = cos((sdeg-90)*0.0174532925);   
    sy = sin((sdeg-90)*0.0174532925);

    if (ss==0 || initial) {
      initial = 0;
      // Erase hour and minute hand positions every minute
      tft.drawLine(ohx, ohy, 120, 121, BLACK);
      ohx = hx*62+121;   
      ohy = hy*62+121;
      tft.drawLine(omx, omy, 120, 121, BLACK);
      omx = mx*84+120;   
      omy = my*84+121;
    }

    // Redraw new hand positions, hour and minute hands not erased here to avoid flicker
    tft.drawLine(osx, osy, 120, 121, BLACK);
    osx = sx*90+121;   
    osy = sy*90+121;
    tft.drawLine(osx, osy, 120, 121, RED);
    tft.drawLine(ohx, ohy, 120, 121, WHITE);
    tft.drawLine(omx, omy, 120, 121, WHITE);
    tft.drawLine(osx, osy, 120, 121, RED);

    tft.fillCircle(120, 121, 3, RED);
}

void chime() {
  digitalWrite(CHIME,HIGH);
  Serial.println("CHIME HIGH");
  delay(100);
  digitalWrite(CHIME,LOW);
  Serial.println("CHIME LOW");
}

void adjustClock() {
  int i;
  for(i=0;i<30;i++) {
    if (getSNTPtime(buf, 64, 5000)) {
      Serial.println("SNTP Time is [" + String(buf) +"]");
      epoch = makeTime(buf);
      Serial.println("epoch = " + String(epoch));
      if (epoch != 0) {
        setTime(epoch);
        return;
      }
      delay(1000);
    } else {
      errorDisplay("getSNTPtime Fail");
    }
  }
  errorDisplay("getSNTPtime Fail");
}

void setup(void)
{
  if (CHIME) {
    pinMode(CHIME,OUTPUT);
    digitalWrite(CHIME,LOW);
  }
  if (LM393) {
    pinMode(LM393,INPUT);
  }

  delay(1000);
  Serial.begin(9600);

  //Make sure ESP8266 is set to 4800
  Serial2.begin(4800);

  //Enable autoconnect
  sendCommand("AT+CWAUTOCONN=1");
  if (!waitForString("OK", 2, 10000)) {
    errorDisplay("AT+CWAUTOCONN Fail");
  }
  clearBuffer();

  //Restarts the Module
  sendCommand("AT+RST");
  if (!waitForString("WIFI GOT IP", 11, 10000)) {
    errorDisplay("ATE+RST Fail");
  }
  clearBuffer();

  //Local echo off
  sendCommand("ATE0");
  if (!waitForString("OK", 2, 1000)) {
    errorDisplay("ATE0 Fail");
  }
  clearBuffer();

  if (_DEBUG_) {
    //Get local IP address
    sendCommand("AT+CIPSTA?");
    if (!waitForString("OK", 2, 1000)) {
      errorDisplay("AT+CIPSTA? Fail");
    }
    clearBuffer();
  }
 
  //Sets the Configuration of SNTP
  sprintf(buf,"AT+CIPSNTPCFG=1,%d\r\n",TIME_ZONE);
  sendCommand(buf);
  if (!waitForString("OK", 2, 1000)) {
    errorDisplay("AT+CIPSNTPCFG Fail");
  }
  clearBuffer();

  adjustClock();
  Serial.print("now=" + String(year()) + "/" + String(month()) + "/" + String(day()));
  Serial.println(" " + String(hour()) + ":" + String(minute()) + ":" + String(second()));

  uint16_t ID = tft.readID();
  Serial.print("Device ID: 0x"); Serial.println(ID, HEX);
  tft.begin(ID);
  uint16_t wid = tft.width();
  uint16_t ht = tft.height();
  Serial.println("width:" + String(wid) + " height:" + String(ht));

  char dateStr[11];
  sprintf(dateStr,"%04d/%02d/%02d",year(),month(),day());
  drawWatch(dateStr);

  Serial.println("Wait for a moment");
  uint8_t h2 = hour();
  if (h2 > 11) h2 = h2 - 12;
  for(int hhh=0;hhh<h2;hhh++) {
    for(int mmm=0;mmm<60;mmm++) {
      drawTime(hhh,mmm,0);
      delay(10);
    }
  }
  for(int mmm=0;mmm<=minute();mmm++) {
    drawTime(hour(),mmm,0);
    delay(10);
  }
  for(int sss=0;sss<=second();sss++) {
    drawTime(hour(),minute(),sss);
    delay(10);
  }
  targetTime = millis() + 1000;
}

void loop() {
  char dateStr[11];
  uint8_t hh,mm,ss;
  static int s_old =1;
  static int s_new;

  if (targetTime < millis()) {
    targetTime = millis()+1000;
    hh = hour();
    mm = minute();
    ss = second();
    Serial.println(String(hh) + ":" + String(mm) + ":" + String(ss));

    if ( (hh == 00 && mm == 0 && ss == 0) ||
         (hh == 06 && mm == 0 && ss == 0) ||
         (hh == 12 && mm == 0 && ss == 0) ||
         (hh == 18 && mm == 0 && ss == 0)) {
      adjustClock();
      if (s_old) {
        if (CHIME) chime();
        char dateStr[11];
        sprintf(dateStr,"%04d/%02d/%02d",year(),month(),day());
        tft.fillScreen(BLACK);
        drawWatch(dateStr);
      }
    }
    if (s_old) drawTime(hh, mm, ss);

    // Read CDS
    if (LM393) {
      s_new = digitalRead(LM393);
     
      Serial.println("s_old=" + String(s_old) + " s_new=" + String(s_new));
      if (s_old ==1 && s_new == 0) {
        Serial.println("Display off");
        tft.fillScreen(BLACK);
      }
      if (s_old ==0 && s_new == 1) {
        Serial.println("Display on");
        char dateStr[11];
        sprintf(dateStr,"%04d/%02d/%02d",year(),month(),day());
        drawWatch(dateStr);
      }
      s_old = s_new;
    }

  }
}

こんな感じで動きます。
初めてYouTubeにアップロードしてみました。