2019년 12월 2일 월요일

Get number of new mails from GMail on Wemos D1 mini ESP8266

Wemos D1 mini ESP8266 을 이용해서 Gmail 의 새편지 갯수 구하기


ESP8266 에서 Gmail 접속을 위해서는 Gmail 의 보안 수준을 설정해야 한다.
https://myaccount.google.com/lesssecureapps 에서 'ON'
8x8 도트매트릭스 MAX7219는 SPI로 통신하는데 핀제어로 자료를 보낸다.
5V    - MAX7219 VCC
GND - MAX7219 GND
PIN2 - MAX7219 DIN (MOSI)
PIN0 - MAX7219 CS
PIN4 - MAX7219 CLK
전원인가후 WiFi 에 접속하고, Gmail 에 접속해서 새편지 갯수를 가져온다.
새편지가 있으면 숫자를 보여주고 새편지가 없으면 점을 하나 찍는다.
약 2분마다 새편지 확인을 반복한다.



#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WiFiClientSecure.h>

#define GMAIL_ID_PW  "id@gmail.com:password"
#define WIFI_SSID    "wifi_ssid"
#define WIFI_PW      "wifi_password"

#define MAX7219_CS   0
#define MAX7219_DIN  2
#define MAX7219_CLK  4

const byte EngFont[8*17] = {
    0x00,0x7E,0xFF,0x81,0x81,0xFF,0x7E,0x00,    // Number 0
    0x00,0x00,0x21,0x7F,0xFF,0x01,0x00,0x00,    // Number 1
    0x00,0x43,0xC7,0x8D,0x99,0xF1,0x61,0x00,    // Number 2
    0x00,0x42,0xC3,0x91,0x91,0xFF,0x6E,0x00,    // Number 3
    0x00,0x0C,0x1C,0x34,0x64,0xFF,0x04,0x00,    // Number 4
    0x00,0xF2,0xF3,0x91,0x91,0x9F,0x8E,0x00,    // Number 5
    0x00,0x7E,0xFF,0x91,0x91,0xDF,0x4E,0x00,    // Number 6
    0x00,0x80,0x80,0x8F,0xBF,0xF0,0xC0,0x00,    // Number 7
    0x00,0x6E,0xFF,0x91,0x91,0xFF,0x6E,0x00,    // Number 8
    0x00,0x70,0xF9,0x89,0x89,0xFF,0x7E,0x00,    // Number 9
    0x00,0x00,0x00,0x06,0x06,0x00,0x00,0x00,    // WiFi .
    0x00,0x08,0x10,0x16,0x16,0x10,0x08,0x00,    // WiFi o
    0x20,0x48,0x50,0x56,0x56,0x50,0x48,0x20,    // WiFi O
    0x7E,0x62,0x52,0x4A,0x4A,0x52,0x62,0x7E,    // Mail check
    0xF8,0xA8,0xA8,0x00,0x1F,0x12,0x12,0x0C,    // Error Parse
    0xF8,0xA8,0xA8,0x00,0x0E,0x11,0x11,0x0A,    // Error Connect
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00     // Empty
};

ESP8266WiFiMulti gWifi;
int gStatus;
int gTic;
int gNum;
byte mPrevChar;

void Max7219Command (byte reg, byte data) {
    digitalWrite (MAX7219_CS, LOW);
    for (byte i=0; i<8; i++) {
        digitalWrite (MAX7219_CLK, LOW);
        digitalWrite (MAX7219_DIN, (reg<<i)&128);
        digitalWrite (MAX7219_CLK, HIGH);
    }
    for (byte i=0; i<8; i++) {
        digitalWrite (MAX7219_CLK, LOW);
        digitalWrite (MAX7219_DIN, (data<<i)&128);
        digitalWrite (MAX7219_CLK, HIGH);
    }
    digitalWrite (MAX7219_CS,  HIGH);
    digitalWrite (MAX7219_DIN, HIGH);
}

void Max7219Put (byte c) {
    mPrevChar = c;
    for (int i=0; i<8; i++) {
        if (c < 64) Max7219Command (8-i, EngFont[c*8+i]);
        else Max7219Command (8-i, ((c>>3)&7)==i ? 128>>(c&7) : 0);
    }
}

void Max7219Fade (byte c) {
    byte a, b, o=mPrevChar;
    delay (0);
    for (a=1; a<32; a++) {
        for (b=a; b<32; b++) Max7219Put (o);
        for (b=0; b< a; b++) Max7219Put (c);
    }
}

void Max7219Bf (byte c) {
    Max7219Fade (16);
    Max7219Fade (c);
}

const char int2base[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
void Text2Base64 (const char *rp, char *wp) {
    int i, len, bits;
    for (len=strlen(rp); 0<len; len-=3) {
        for (i=0; i<3; i++) ((char *)&bits)[2-i] = (i < len) ? *rp++ : 0;
        for (i=0; i<4; i++) *wp++ = (len < i) ? '=' : int2base[(bits>>((3-i)*6))&63];
    }
    *wp = 0;
}

int GetGmailHttp () {
    WiFiClientSecure client;
    char base64[64];
    int num;
    
    client.setInsecure ();
    if (!client.connect ("mail.google.com", 443)) return 15;
    
    Text2Base64 (GMAIL_ID_PW, base64);
    client.print (
        "GET /mail/feed/atom HTTP/1.1\r\n"
        "Host: mail.google.com\r\n"
        "Connection: close\r\n"
        "Authorization: Basic "
    );
    client.print (base64);
    client.print ("\r\n\r\n");
    
    num = 14;
    while (client.connected ()) {
        client.readStringUntil ('<');
        String tn = client.readStringUntil ('<');
        if (tn == "fullcount") {
            num = client.readStringUntil ('<').toInt ();
            if (9 < num) num = 9;
            break;
        }
    }
    client.stop ();
    return num;
}

void loop() {
    delay (1000);
    
    if (gWifi.run () != WL_CONNECTED) {
        gStatus = 0;
        gTic = (gTic+1) % 3;
        Max7219Fade (10+gTic);
        return;
    }
    
    if (gStatus == 0) {
        gStatus = 1;
        gTic = -1;
        gNum = -1;
    }
    
    gTic = (gTic + 1) & 63;
    if (gNum == 0) Max7219Bf (64+gTic);
    if (gTic) {
        if (0 < gNum) Max7219Bf (gNum);
        return;
    }
    
    if (gNum) Max7219Bf (13);
    gNum = GetGmailHttp ();
    if (gNum == 0) Max7219Bf (64+(++gTic));
    else Max7219Bf (gNum);
}

void setup() {
    gStatus = 0;
    gTic =  0;
    pinMode (MAX7219_DIN, OUTPUT);
    pinMode (MAX7219_CLK, OUTPUT);
    pinMode (MAX7219_CS,  OUTPUT);
    digitalWrite (MAX7219_DIN, HIGH);
    digitalWrite (MAX7219_CLK, HIGH);
    digitalWrite (MAX7219_CS,  HIGH);
    delay (50);
    
    Max7219Command (12, 1);
    Max7219Command (11, 7);
    Max7219Command ( 9, 0);
    Max7219Command (15, 0);
    Max7219Command (10, 0);
    Max7219Command (12, 1);
    Max7219Put (16);
    gWifi.addAP (WIFI_SSID, WIFI_PW);
    delay (500);
    
    Max7219Fade (10);
}

2019년 10월 31일 목요일

ES32 + Android 구취측정기

ESP32 를 이용해서 취득한 측정값을 Android App 에서 BLE 방식으로 자료를 가져와서 보여준다.




2019년 4월 1일 월요일

Wemos D1 mini ESP8266 WiFiClient NonBlocking

Wemos D1 mini : 저렴한 가격 + 작은 크기 + WiFi + Arduino

WiFi 로 Server 에 접속할때 connect 명령에서 최대 5초간 멈춤이 발생한다.
NonBlocking, Asynchronous connect 명령을 만들었다.

해결방법:

esp8266 라이브러리 폴더를 찾는다.
C:/Users/UserID/AppData/Local/Arduino15/packages/esp8266/hardware/esp8266/2.5.0/libraries/ESP8266WiFi/src

1. WiFiClient.h 59line 에 추가.
    int connectAsync(const char* host, uint16_t port);
    int connectCheck();

2. WiFiClient.cpp 182line 에 추가.
    int WiFiClient::connectAsync(const char* host, uint16_t port) {
        IPAddress ip;
        if (!WiFi.hostByName(host, ip, _timeout)) return 0;
        if (_client) {
            stop();
            _client->unref();
            _client = nullptr;
        }
    #if LWIP_VERSION_MAJOR == 1
        netif* interface = ip_route(ip);
        if (!interface) {
            DEBUGV("no route to host\r\n");
            return 0;
        }
    #endif
        tcp_pcb* pcb = tcp_new();
        if (!pcb) return 0;
        if (_localPort > 0)  pcb->local_port = _localPort++;
        _client = new ClientContext(pcb, nullptr, nullptr);
        _client->ref();
        _client->setTimeout(_timeout);
        int res = _client->connectAsync(ip, port);
        if (res == 0) {
            _client->unref();
            _client = nullptr;
            return 0;
        }
        return 1;
    }
    int WiFiClient::connectCheck() {
        int res = _client->connectCheck();
        if (res == 0) return 0;
        if (res == -1) {
            _client->unref();
            _client = nullptr;
            return -1;
        }
        setSync(defaultSync);
        setNoDelay(defaultNoDelay);
        return 1;
    }

3. include/ClientContext.h 147line 에 추가.
    int connectAsync(CONST ip_addr_t* addr, uint16_t port) {
        err_t err = tcp_connect (_pcb, addr, port, &ClientContext::_s_connected);
        if (err != ERR_OK) return 0;
        _op_start_time = millis();
        return 1;
    }
    int connectCheck() {
        if (!_pcb) return -1;
        if (_pcb->state == ESTABLISHED) return 1;
        if (millis() - _op_start_time < _timeout_ms) return 0;
        abort();
        return -1;
    }

4. include/ClientContext.h 수정
    함수
        err_t _connected(struct tcp_pcb *pcb, err_t err)
    
        assert(_connect_pending);
        esp_schedule();
    
        if (_connect_pending) esp_schedule();
    로 변경

사용방법:

    WiFiClient client;
    int rv;
    if (client.connectAsync ("192.168.1.2", 123) == 0) return -1;
    do {
        rv = client.connectCheck ();
        if (rv == -1) return -1;
        if (rv ==  1) break;
        delay (1);
    } while (rv == 0);
    
    len = client.available ();
    if (0 < len) len = client.read (buff, len);
    
    len = client.availableForWrite ();
    if (0 < len) len = client.write (buff, len);