개발자/Arduino

OpenWeatherMap 날씨 정보를 OLED 에 디스플레이, Nano 33 IoT

지구빵집 2020. 10. 24. 22:03
반응형

 

 

Nano 33 IoT 보드를 사용하여 원하는 도시의 온도와 습도 정보를 보여줍니다. 날씨 정보를 뽑아오는 상세한 설명은 참고 자료의 사이트를 참고합니다. 세계 어떤 지역의 날씨를 보여줄 수 있습니다. 아래 첨부한 잘 돌아가는 코드를 참고하세요.

 

보완할 점

 

1. 장소를 옮길 때마다 매번 인터넷 WiFi 변경을 바꿔주고 펌웨어를 업로드해야 한다. 블루투스로 연결하여 설정을 바꿔주면 좋겠다. 설정 정보는 wifi 접속 정보, 국가, 도시, 보여줄 정보 - 온도, 습도, 압력, 바람 등-를 사용자가 선택하게.

 

2. 예쁜 기상 정보 아이콘은 Weather Icons란 사이트를 참고한다.

 

3. display 화면이 현재는 ssd1306 oled인데 너무 작고 정보가 적으므로 큰 디스플레이 화면으로 변경

 

아래 잘 돌아가는 코드를 참고합니다.

 

WiFi 환경을 잡아주는 arduino_secret.h 파일을 인터넷 환경에 맞도록 수정한다.

 

#define SECRET_SSID "FO****RCM"
#define SECRET_PASS "166******10350"

 

API 키를 받아와 OpenweatherMap 사이트에서 정보를 불러와 OLED에 나타내는 코드입니다. 25라인 보면 발급받은 API 키를 이용해 도시 설정을 바꾸어 주면 됩니다. 예제에서는 그린란드의 qaanaaq 란 도시의 온도와 습도를 보여줍니다. 결과 화면을 아래에 첨부합니다.

 

/*
 * Rui Santos 
 * Complete Project Details http://randomnerdtutorials.com
 * Based on the Arduino Ethernet Web Client Example
 * and on the sketch "Sample Arduino Json Web Client" of the Arduino JSON library by Benoit Blanchon (bblanchon.github.io/ArduinoJson)
 */

#include <ArduinoJson.h>
#include <SPI.h>
#include <WiFiNINA.h>

#include <Adafruit_GFX.h>      // include Adafruit graphics library
#include <Adafruit_SSD1306.h>  // include Adafruit SSD1306 OLED display driver

#define OLED_RESET  4    // define display reset pin
Adafruit_SSD1306 display(OLED_RESET);

//EthernetClient client;
WiFiClient client;

// Name address for Open Weather Map API
const char* server = "api.openweathermap.org";

// Replace with your unique URL resource
//const char* resource = "/data/2.5/weather?q=seoul,KR&APPID=34b3527445f4bb5a2b~~~~~~~~~~~~~~";
//const char* resource = "/data/2.5/weather?q=busan,KR&APPID=34b3527445f4bb5a2b~~~~~~~~~~~~~~";
const char* resource = "/data/2.5/weather?q=qaanaaq&APPID=34b3527445f4bb5a2b8be~~~~~~~~~~~~~~";

// How your resource variable should look like, but with your own COUNTRY CODE, CITY and API KEY (that API KEY below is just an example):
//const char* resource = "/data/2.5/weather?q=Porto,pt&appid=bd939aa3d23ff33d3c8f5dd1";

const unsigned long HTTP_TIMEOUT = 10000;  // max respone time from server
const size_t MAX_CONTENT_SIZE = 512;       // max size of the HTTP response


#include "arduino_secrets.h" 
///////please enter your sensitive data in the Secret tab/arduino_secrets.h
char ssid[] = SECRET_SSID;        // your network SSID (name)
char pass[] = SECRET_PASS;    // your network password (use for WPA, or use as key for WEP)
int keyIndex = 0;            // your network key Index number (needed only for WEP)

int status = WL_IDLE_STATUS;

// The type of data that we want to extract from the page
struct clientData {
  char temp[8];
  char humidity[8];
};

// ARDUINO entry point #1: runs once when you press reset or power the board
void setup() {
  Serial.begin(9600);
  //while (!Serial) { ; } //wait for serial port to initialize

  initial_oled();
  
  Serial.println("Serial ready");
  while (status != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
    status = WiFi.begin(ssid, pass);

    // wait 10 seconds for connection:
    delay(10000);
  }
  Serial.println("Connected to wifi");
  printWifiStatus();
}

// ARDUINO entry point #2: runs over and over again forever
void loop() {
  if(connect(server)) {
    if(sendRequest(server, resource) && skipResponseHeaders()) {
      clientData clientData;
      if(readReponseContent(&clientData)) {
        printclientDatatoserial(&clientData);
        printclientDatatooled(&clientData);        
      }
    }
  }
  disconnect();
  wait();
}

// Open connection to the HTTP server
bool connect(const char* hostName) {
  Serial.print("Connect to ");
  Serial.println(hostName);

  bool ok = client.connect(hostName, 80);

  Serial.println(ok ? "Connected" : "Connection Failed!");
  return ok;
}

// Send the HTTP GET request to the server
bool sendRequest(const char* host, const char* resource) {
  Serial.print("GET ");
  Serial.println(resource);

  client.print("GET ");
  client.print(resource);
  client.println(" HTTP/1.1");
  client.print("Host: ");
  client.println(host);
  client.println("Connection: close");
  client.println();

  return true;
}

// Skip HTTP headers so that we are at the beginning of the response's body
bool skipResponseHeaders() {
  // HTTP headers end with an empty line
  char endOfHeaders[] = "\r\n\r\n";

  client.setTimeout(HTTP_TIMEOUT);
  bool ok = client.find(endOfHeaders);

  if (!ok) {
    Serial.println("No response or invalid response!");
  }
  return ok;
}

// Parse the JSON from the input string and extract the interesting values
// Here is the JSON we need to parse
/*{
    "coord": {
        "lon": -8.61,
        "lat": 41.15
    },
    "weather": [
        {
            "id": 800,
            "main": "Clear",
            "description": "clear sky",
            "icon": "01d"
        }
    ],
    "base": "stations",
    "main": {
        "temp": 296.15,
        "pressure": 1020,
        "humidity": 69,
        "temp_min": 296.15,
        "temp_max": 296.15
    },
    "visibility": 10000,
    "wind": {
        "speed": 4.6,
        "deg": 320
    },
    "clouds": {
        "all": 0
    },
    "dt": 1499869800,
    "sys": {
        "type": 1,
        "id": 5959,
        "message": 0.0022,
        "country": "PT",
        "sunrise": 1499836380,
        "sunset": 1499890019
    },
    "id": 2735943,
    "name": "Porto",
    "cod": 200
}*/

bool readReponseContent(struct clientData* clientData) {
  // Compute optimal size of the JSON buffer according to what we need to parse.
  // See https://bblanchon.github.io/ArduinoJson/assistant/
  const size_t bufferSize = JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(1) + 
      2*JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(4) + JSON_OBJECT_SIZE(5) + 
      JSON_OBJECT_SIZE(6) + JSON_OBJECT_SIZE(12) + 390;
  DynamicJsonBuffer jsonBuffer(bufferSize);

  JsonObject& root = jsonBuffer.parseObject(client);

  if (!root.success()) {
    Serial.println("JSON parsing failed!");
    return false;
  }

  // Here were copy the strings we're interested in using to your struct data
  strcpy(clientData->temp, root["main"]["temp"]);
  strcpy(clientData->humidity, root["main"]["humidity"]);
  // It's not mandatory to make a copy, you could just use the pointers
  // Since, they are pointing inside the "content" buffer, so you need to make
  // sure it's still in memory when you read the string

  return true;
}

// Print the data extracted from the JSON
void printclientDatatoserial(const struct clientData* clientData) {

  Serial.println(clientData->temp);
  Serial.println(clientData->humidity);
    
  Serial.print("Temp = ");
  String receivedTemp = clientData->temp;
  Serial.print(receivedTemp.toFloat()-273.15);
  Serial.println(" ℃");
  Serial.print("Humidity = ");
  Serial.print(clientData->humidity);
  Serial.println(" %");
}

char _readydatatoOled[8];

void printclientDatatooled(const struct clientData* clientData) {
  String receivedTemp = clientData->temp;
  float tCelsius = receivedTemp.toFloat()-273.15;
  display.setCursor(23, 10);
  display.print(String(tCelsius) + " C");
  display.setCursor(23, 30);
  display.print(String(clientData->humidity) + " /");

  display.drawCircle(76, 12, 2, WHITE);
  display.drawCircle(60, 32, 2, WHITE); 
  display.drawCircle(70, 42, 2, WHITE); 
  
  display.display();
}

// Close the connection with the HTTP server
void disconnect() {
  Serial.println("Disconnect");
  client.stop();
}

// Pause for a 1 minute
void wait() {
  Serial.println("Wait 60 seconds");
  delay(60000);
}


void printWifiStatus() {
  // print the SSID of the network you're attached to:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // print your board's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  // print the received signal strength:
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
}

void initial_oled()
{
  //initialize the SSD1306 OLED display with I2C address = 0x3D
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);

  // clear the display buffer.
  display.clearDisplay();
 
  display.setTextSize(1);   // text size = 1
  display.setTextColor(WHITE, BLACK);  // set text color to white and black background
  display.setCursor(15, 0);            // move cursor to position (15, 0) pixel
  display.print("Seoul Weather");
  display.display();        // update the display
  display.setTextSize(2);   // text size = 2
}

 

결과 사진

 

qaanaaq 도시의 온도와 습도를 보여준다.

 

 

참고사이트

OpenWeatherMap을 이용한 날씨 API를 사용해보자 

222 Weather Themed Icons and CSS 

 

 

 

반응형