PM2008M 미세먼지 센서 출력을 SSD1306 OLED에 표시하기 - 아두이노 메가사용
디지털 창작집단에서 메이커 활동으로 공기청정기를 메이커 활동을 진행했다. 당시에는 캐릭터 LCD를 사용했는데 이번에 출력을 OLED로 하는 게 좋을 듯해서 다시 코딩.
참고자료 : 레이저 미세먼지 센서 PM2008M 을 아두이노메가 측정 코드와 결과
필요 부품: OLED, 온도 습도 센서, PM2008M 미세먼지 센서, RGB Led, 아두이노 메가
아두이노 메가와 센서를 연결한 회로도
아래 회로도에서 미세먼지 센서 PM2008M 연결도는 위 링크를 참조하여 Serial 포트에 연결한다.
회로도대로 연결하여 각 구성품을 테스트하고 전체를 조합하여 완성한다. 각 구성품과 연결하는 방법과 테스트 하는 문서는 아래 링크를 참고하기 바란다. 문의 사항 환영
[소프트박스 코딩] - 소프트박스에서 온도 습도 센서 데이터 확인하기
[아두이노] - 아두이노 온도 습도 미터기 DHT11 과 OLED display 사용
[소프트박스 코딩] - 소프트박스에서 RGB LED로 컬러 불 켜기
[아두이노] - 아두이노 온도 습도 미터기 DHT11 과 OLED display 사용
[아두이노] - 먼지센서 PM2005, PM2007, PM2008M 아두이노 우노 예제코드
[아두이노] - 레이저 미세먼지 센서 PM2008M 에 대해 알아보자.
[메이커] - 미세먼지 개념과 미세먼지 센서(PM2008M) 알아보기
Fritzing 으로 그린 도면 첨부
일단 잘 동작하는 소스코드 파일 첨부
아래는 소스코드
// Written by EmbeddedUser
// Refer to blog http://www.jaimeferns.in/2016/10/how-to-build-humidity-and-temperature.html
// Youtube Video https://www.youtube.com/watch?v=g-rcEd_2I-w
// Humidity meter with DHT11, Arduino and I2c OLED display
// Now support displaying sensor error and configuring deg C or deg F
#include <DHT.h>
#include "U8glib.h"
#include <SoftwareSerial.h>
#define BT_RX 7 //HM10의 TX에 연결 - 주의
#define BT_TX 8 //HM10의 RX에 연결 - 주의
SoftwareSerial HM10(BT_RX,BT_TX); // RX핀(7번)은 HM10의 TX에 연결
// TX핀(8번)은 HM10의 RX에 연결
// Display defaults to Degree F. TO use metric display,
// comment out next line to display temperature in Degree C,
#define METRIC
#define DHTPIN 2 // what digital pin we're connected to
// Uncomment whatever type you're using!
#define DHTTYPE DHT11 // DHT 11
//#define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321
//#define DHTTYPE DHT21 // DHT 21 (AM2301)
// Connect pin 1 (on the left) of the sensor to +5V
// NOTE: If using a board with 3.3V logic like an Arduino Due connect pin 1
// to 3.3V instead of 5V!
// Connect pin 2 of the sensor to whatever your DHTPIN is
// Connect pin 4 (on the right) of the sensor to GROUND
// Connect a 10K resistor from pin 2 (data) to pin 1 (power) of the sensor
// Initialize DHT sensor.
// Note that older versions of this library took an optional third parameter to
// tweak the timings for faster processors. This parameter is no longer needed
// as the current DHT reading algorithm adjusts itself to work on faster procs.
DHT dht(DHTPIN, DHTTYPE);
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE);
//U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NO_ACK); // Display which does not send AC
//U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE|U8G_I2C_OPT_DEV_0); // I2C / TWI
//U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0|U8G_I2C_OPT_NO_ACK|U8G_I2C_OPT_FAST); // Fast I2C / TWI
//U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NO_ACK); // Display which does not send AC
const uint8_t logo[] PROGMEM =
{
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x18, 0x0, 0x38, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xc3,
0x80, 0x0, 0x0, 0x0, 0x18, 0x0, 0x38, 0x6, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x1,
0xc3, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0xc0, 0x0, 0x0, 0x0, 0x0,
0x1, 0xc3, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0xc0, 0x0, 0x0, 0x0,
0x0, 0x1, 0xc3, 0x8e, 0xc, 0x6e, 0x60, 0xf8, 0x7, 0xb8, 0x3e, 0x3, 0xfc, 0x38, 0x18,
0x0, 0x0, 0x1, 0xc3, 0x8e, 0xc, 0x7e, 0xf0, 0xf8, 0xf, 0xf8, 0x3e, 0x3, 0xfc, 0x38,
0x38, 0x0, 0x0, 0x1, 0xff, 0x8e, 0xc, 0x77, 0x30, 0x18, 0xe, 0x38, 0x6, 0x0, 0xc0,
0x18, 0x30, 0x0, 0x0, 0x1, 0xff, 0x8e, 0xc, 0x67, 0x30, 0x18, 0x1c, 0x38, 0x6, 0x0,
0xc0, 0x1c, 0x30, 0x0, 0x0, 0x1, 0xff, 0x8e, 0xc, 0x63, 0x30, 0x18, 0x1c, 0x38, 0x6,
0x0, 0xc0, 0xc, 0x70, 0x0, 0x0, 0x1, 0xc3, 0x8e, 0xc, 0x63, 0x30, 0x18, 0x1c, 0x18,
0x6, 0x0, 0xc0, 0xc, 0x60, 0x0, 0x0, 0x1, 0xc3, 0x8e, 0xc, 0x63, 0x30, 0x18, 0x1c,
0x18, 0x6, 0x0, 0xc0, 0xe, 0x60, 0x0, 0x0, 0x1, 0xc3, 0x86, 0x1c, 0x63, 0x30, 0x18,
0x1c, 0x18, 0x6, 0x0, 0xc0, 0x6, 0xc0, 0x0, 0x0, 0x1, 0xc3, 0x87, 0x1c, 0x63, 0x30,
0x18, 0xe, 0x38, 0x6, 0x0, 0xe0, 0x7, 0xc0, 0x0, 0x0, 0x1, 0xc3, 0x87, 0xec, 0x63,
0x31, 0xff, 0x8f, 0xd8, 0x7f, 0xe0, 0xfe, 0x3, 0xc0, 0x0, 0x0, 0x1, 0xc3, 0x83, 0xcc,
0x63, 0x31, 0xff, 0x87, 0x98, 0x7f, 0xe0, 0x7e, 0x3, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x3, 0x80, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1e, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x38, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x78, 0x0, 0x1, 0x80, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3c, 0x78, 0x0, 0x1, 0x80, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3e, 0x78, 0x3e, 0x7, 0xf8,
0xf, 0x81, 0xcf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36, 0xd8, 0x7f, 0x7,
0xf8, 0x1f, 0xc0, 0xdf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x36, 0xd8, 0xe1,
0x81, 0x80, 0x38, 0x60, 0xf0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x33, 0xd8,
0xc1, 0xc1, 0x80, 0x30, 0x70, 0xe0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x33,
0x99, 0xff, 0xc1, 0x80, 0x7f, 0xf0, 0xe0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x33, 0x99, 0xff, 0xc1, 0x80, 0x7f, 0xf0, 0xc0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x31, 0x99, 0xc0, 0x1, 0x80, 0x70, 0x0, 0xc0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x30, 0x18, 0xc0, 0x1, 0x80, 0x30, 0x0, 0xc0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x30, 0x18, 0xe1, 0xc1, 0xc0, 0x38, 0x70, 0xc0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x30, 0x18, 0x7f, 0x81, 0xfc, 0x1f, 0xe0, 0xc0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x30, 0x18, 0x3e, 0x0, 0xfc, 0xf, 0x80, 0xc0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0
};
bool first = true;
float hum = 0.0;
float temp = 0.0;
float hIndex = 0.0;
bool dht_test(float* humPerc, float* tempF, float* heatIndex);
int redPin_1 = 5;
int greenPin_1 = 6;
int bluePin_1 = 7;
unsigned char Send_data[5] = {0x11,0x02,0x0B,0x07, 0xDB}; // 농도읽는명령
unsigned char Receive_Buff[56]; // data buffer
unsigned long PM1, PM25, PM10; // 농도저장변수 : 각 32bit(8bit*4 = 32)
unsigned long COM_SUCCESS, COM_COUNT; // 통신성공/통신시도횟수
unsigned char recv_cnt = 0;
void setup(void)
{
Serial.begin(9600);
while (!Serial) ;
Serial2.begin(9600);
while (!Serial2);
dht.begin();
first = true;
pinMode(redPin_1, OUTPUT);
pinMode(greenPin_1, OUTPUT);
pinMode(bluePin_1, OUTPUT);
//BLE
HM10.begin(9600);
// assign default color value
if (u8g.getMode() == U8G_MODE_R3G3B2)
{
u8g.setColorIndex(255); // white
}
else if (u8g.getMode() == U8G_MODE_GRAY2BIT)
{
u8g.setColorIndex(3); // max intensity
}
else if (u8g.getMode() == U8G_MODE_BW)
{
u8g.setColorIndex(1); // pixel on
}
else if (u8g.getMode() == U8G_MODE_HICOLOR)
{
u8g.setHiColorByRGB(255, 255, 255);
}
// picture loop
u8g.firstPage();
do
{
u8g.drawBitmapP(0, 0, 16, 64, logo);
}
while (u8g.nextPage());
dht_test(&hum, &temp, &hIndex);
}
void HumMeter(float* humPerc, float* temp, float* heatIndex)
{
u8g.setFont(u8g_font_fub11);
u8g.setFontRefHeightExtendedText();
u8g.setDefaultForegroundColor();
u8g.setFontPosTop();
u8g.drawStr(4, 0, "PM2.5 ");
u8g.setPrintPos(80, 0);
//u8g.print(*humPerc);
u8g.print(PM25);
/*#ifdef METRIC
// if metric system, display Celsius
u8g.drawStr(4, 20, "Temp C");
#else
//display Farenheit
u8g.drawStr(4, 20, "Temp F");
#endif
*/
u8g.drawStr(4, 20, "PM10 ");
u8g.setPrintPos(80, 20);
u8g.print(PM10);
//u8g.drawStr(4, 40, "Heat Ind");
u8g.setPrintPos(4, 40);
u8g.print((int)*temp);
u8g.drawStr(30, 40, "C");
u8g.setPrintPos(80, 40);
u8g.print((int)*humPerc);
u8g.drawStr(100, 40, "%");
}
void loop(void)
{
bool result = dht_test(&hum, &temp, &hIndex);
airquality_rgb();
Serial.print("here ");
display_pm10();
Serial.print("here2 ");
//for ble test
/*if (HM10.available()) {
Serial.write(HM10.read());
}
if (Serial.available()) {
HM10.write(Serial.read());
}*/
if (first)
{
// skip displaying readings first time, as its stale data.
first = false;
}
else
{
if(result == false)
{
u8g.firstPage();
do
{
// sensor error
u8g.setFont(u8g_font_fub11);
u8g.setFontRefHeightExtendedText();
u8g.setDefaultForegroundColor();
u8g.setFontPosTop();
u8g.drawStr(10, 30, "Sensor Error");
}
while (u8g.nextPage());
}
else
{
u8g.firstPage();
do
{
HumMeter(&hum, &temp, &hIndex);
}
while (u8g.nextPage());
}
}
}
void airquality_rgb()
{
if(PM25 <= 15)
setColor(255, 0, 0); // cyan
if((PM25>15) && (PM25<=35))
setColor(255, 0, 255); // green
if((PM25>35) && (PM25<=75))
setColor(0, 0, 255); // yello
if(PM25>75)
setColor(0, 255, 255); // red
}
void setColor(int red, int green, int blue)
{
digitalWrite(redPin_1, red);
digitalWrite(greenPin_1, green);
digitalWrite(bluePin_1, blue);
}
void Send_CMD(void) // COMMAND
{
unsigned char i;
for(i=0; i<5; i++)
{
Serial2.write(Send_data[i]);
delay(1); // Don't delete this line !!
}
}
unsigned char Checksum_cal(void) // CHECKSUM
{
unsigned char count, SUM=0;
for(count=0; count<55; count++)
{
SUM += Receive_Buff[count];
}
return 256-SUM;
}
bool dht_test(float* humPerc, float* temp, float* heatIndex)
{
// Wait a few seconds between measurements.
delay(2000);
*humPerc = 0;
*temp = 0;
*heatIndex = 0;
// Reading temperature or humidity takes about 250 milliseconds!
// Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
float h = dht.readHumidity();
// Read temperature as Celsius (the default)
float t = dht.readTemperature();
// Read temperature as Fahrenheit (isFahrenheit = true)
float f = dht.readTemperature(true);
// Check if any reads failed and exit early (to try again).
if (isnan(h) || isnan(t) || isnan(f))
{
Serial.println("Failed to read from DHT sensor!");
return false;
}
// Compute heat index in Fahrenheit (the default)
float hif = dht.computeHeatIndex(f, h);
// Compute heat index in Celsius (isFahreheit = false)
float hic = dht.computeHeatIndex(t, h, false);
Serial.print("Humidity: ");
Serial.print(h);
Serial.print(" %\t");
Serial.print("Temperature: ");
Serial.print(t);
Serial.print(" *C ");
Serial.print(f);
Serial.print(" *F\t");
Serial.print("Heat index: ");
Serial.print(hic);
Serial.print(" *C ");
Serial.print(hif);
Serial.println(" *F");
*humPerc = h;
#ifdef METRIC
// metric system, load degree celsius
*temp = t;
*heatIndex = hic;
#else
*temp = f;
*heatIndex = hif;
#endif
return true;
//send humi temp via BLE
if (Serial.available()) {
//HM10.write(*temp);
HM10.write(Serial.read());
}
}
void display_pm10()
{
COM_COUNT++;
Send_CMD(); // Send Read Command
while(1)
{
if(Serial2.available())
{
Receive_Buff[recv_cnt++] = Serial2.read();
if(recv_cnt ==56){recv_cnt = 0; break;}
}
}
if(Checksum_cal() == Receive_Buff[55]) // CS 확인을 통해 통신 에러 없으면
{
COM_SUCCESS++;
PM1 = (unsigned long)Receive_Buff[3]<<24 | (unsigned long)Receive_Buff[4]<<16 | (unsigned long)Receive_Buff[5]<<8| (unsigned long)Receive_Buff[6]; // 농도계산(시프트)
PM25 = (unsigned long)Receive_Buff[7]<<24 | (unsigned long)Receive_Buff[8]<<16 | (unsigned long)Receive_Buff[9]<<8| (unsigned long)Receive_Buff[10]; // 농도계산(시프트)
PM10 = (unsigned long)Receive_Buff[11]<<24 | (unsigned long)Receive_Buff[12]<<16 | (unsigned long)Receive_Buff[13]<<8| (unsigned long)Receive_Buff[14]; // 농도계산(시프트)
Serial.write("COM count : ");
Serial.print(COM_SUCCESS);
Serial.write(" / ");
Serial.print(COM_COUNT);
Serial.write(" PM1.0 : ");
Serial.print(PM1);
Serial.write(" PM2.5 : ");
Serial.print(PM25);
Serial.write(" PM10 : ");
Serial.println(PM10);
}
else
{
Serial.write("CHECKSUM Error");
}
}
'메이커' 카테고리의 다른 글
최선의 삶을 산다는 것 (0) | 2023.04.27 |
---|---|
IoT(사물인터넷) 웨더스테이션(IoT Local Weather Station) (0) | 2020.03.02 |
라즈베리파이 기반 IoT(사물인터넷) 프로그래밍 과정 안내 (0) | 2020.03.02 |
미세먼지 알림 신호등 메이커 프로젝트 (0) | 2020.03.02 |
IoT(사물인터넷) 스마트팜 메이커 프로젝트 요약 (0) | 2020.03.02 |