'bme280'에 해당되는 글 3건

  1. 2018.11.20 Hardware | RTC DS3231 부품 사용기 - 2
  2. 2017.07.12 Hardware | AM2322 Temperature & Humidity Sensor
  3. 2017.03.09 Hardware | BME280 sensor

Hardware | RTC DS3231 부품 사용기 - 2

|

이 포스트는 앞전에 DS3231 을 사용해 보면서, 못다한 이야기를 하기 위해 구성했습니다.


* Hardware | RTC DS3231 부품 사용기 - 1

http://chocoball.tistory.com/entry/Hardware-RTC-usning-DS3231-1



1. AT24C32 (EEPROM) I2C address


DS3231 에 붙어있는 EEPROM 은 ATMEL 사의 AT24C32 라는 칩 입니다.

32bit = 4kByte EEPROM 의 I2C 기본 주소값은 0x57 입니다.


 

 A0

A1

A2 

0x57

0

0

0

0x56

1

0

0

0x55

0

1

0

0x54

1

1

0

0x53

0

0

1

0x52

1

0

1

0x51

0

1

1

0x50

1

1

1

* 0 = open, 1 = short


참고로 0x57 은 16진수 이므로, 10진수로 표시하고자 할 때에는 87 이라고 입력해도 됩니다.


* Hex to Decimal converter

https://www.rapidtables.com/convert/number/hex-to-decimal.html






2. 유령 I2C address - 0x5f


제가 사용한 fake DS3231 은, 사용되지 않는 "0x5f" 라는 I2C 주소가 존재합니다.



모르고 지나가기엔 너무 궁금합니다.

여러 방법으로 찾아 봤는데, 가장 신뢰성 있는 설명은 다음 LINK 인 듯 합니다.


* I2C response to "Ghost Address" 0x5F

https://electronics.stackexchange.com/questions/397569/i2c-response-to-ghost-address-0x5f



즉, 정품에 포함되어 있는 32k EEPROM 은 ATMEL 문자로 시작하는데,

이 fake 제품에 다른 chip 이 있는 것이 아니라, ATMLH745 는 address 가 두개 있는 듯 합니다.


왜냐하면, 위의 첫번째 댓글에 A2 단자를 short 시켰을때 EEPROM 주소가 바뀌는데,

이 ghost address 도 연동해서 바뀐다는 테스트 결과가 나왔거든요.


A0, A1, A2 all open (default)
--------------------------------------------
|   chip  |    DIGIT   |   HEX  |  DECIMAL |
--------------------------------------------
| AT24C32 | 0b01010111 |  0x57  |    87    |
--------------------------------------------
|  ghost  | 0b01011111 |  0x5f  |    95    |
--------------------------------------------

A0, A1 open, A2 short
--------------------------------------------
|   chip  |    DIGIT   |   HEX  |  DECIMAL |
--------------------------------------------
| AT24C32 | 0b01010011 |  0x53  |    83    |
--------------------------------------------
|  ghost  | 0b01011011 |  0x5b  |    91    |
--------------------------------------------


그럼 AT24C32 주소를 0x5f 로 해보면 어떨까요?

아래처럼 소스를 조금 바꿔 봤습니다.


#include "Wire.h"
#define AT24C32_I2C_ADDRESS 0x5f //I2C address of AT24C32

byte seconds, minutes, hours, day, date, month, year;

void setup() {
	Serial.begin(9600);
	delay(1000);
	
	Wire.begin();
}

void loop() {
	getAT24C32Data();
	
	Serial.print("seconds : "); Serial.println(seconds, BIN);
	Serial.print("minutes : "); Serial.println(minutes, BIN);
	Serial.print("hours   : "); Serial.println(hours, BIN);
	Serial.print("day     : "); Serial.println(day, BIN);
	Serial.print("date    : "); Serial.println(date, BIN);
	Serial.print("month   : "); Serial.println(month, BIN);
	Serial.print("year    : "); Serial.println(year, BIN); 
	Serial.println("");
	
	delay(1000);
}

void getAT24C32Data() {
	// send request to receive data starting at register 0
	Wire.beginTransmission(AT24C32_I2C_ADDRESS);
	Wire.write(0x00); // start at register 0
	Wire.endTransmission();
	Wire.requestFrom(AT24C32_I2C_ADDRESS, 7); // request seven bytes
	
	if(Wire.available()) {
		seconds = Wire.read(); // get seconds
		minutes = Wire.read(); // get minutes
		hours   = Wire.read(); // get hours
		day     = Wire.read();
		date    = Wire.read();
		month   = Wire.read(); //temp month
		year    = Wire.read();
	} else {
		//oh noes, no data!
	}
}


원래 AT24C32 의 주소인 0x57 를 넣으면 아래처럼 나옵니다만,

0x5f 로 하면 data 는 오는데, 전부 255 값을 갖습니다.


0x57 로 할 때



0x5f 로 할 때



엄한 어드레스인 0x83 로 할 때



값이 동일하지 않아서, 완벽히 동일한 값에 접근하는것 같지는 않습니다.

그래도 뭔가 있기는 한것 같네요.


소설을 써 보자면, 중국에서 fake 칩을 만들면서 다른 기능도 넣지 않았을까...

혹시 어떤 다른 정보와 관여하는 기능이 있는것이 아닌지?





3. Battery 의 종류


밧데리는 지금 CR2032 을 끼워서 사용하고 있습니다.

시간 유지만으로 사용되면 1uA 정도 사용되므로, 200mAh 라고 한다면 약 20년은 사용 가능하다고 하네요.


* RTCモジュール DS3231+AT24C32 ZS-042

https://blogs.yahoo.co.jp/dascomp21/68145713.html



다만, 사양적으로는 충방전이 되는 LIR2032 을 사용하라고 하네요.



실제로 약 1년정도 사용하다가 베터리가 터진 케이스가 인터넷에 보고되었습니다.


회로 구성도를 봐도, 충전이 되게끔 만들어진 회로라서 rechargeable battery 를 사용하지 않으면,

지속적으로 건전지에 부담이 가게끔 되어 있다고 합니다.



굳이 CR2025 을 장기간 사용하려 한다면, 위의 그림처럼 방충전 회로와 연결되는 패턴을 끊어주면 된다 합니다.


잠깐 쓰고 빼 놓거나, 잔량이 거의 없는거라면 괜찮겠죠?

저는 체중계에서 다 쓴 건전지를 사용하고 있어서 그냥 낑궈 놓으려고 합니다.


이 정보는 아래 사이트를 참고하였습니다.


* RTC DS3231/DS1302を調べて見ました

https://blogs.yahoo.co.jp/hobbyele/64900109.html





4. BCD


EEPROM 에 집어 넣고 빼는 값의 포맷은 decimal 이지만, 실제로 저장되는 값은 binary 나열값 입니다.


더 나아가, 이는 아래 그림처럼 EEPROM 에 저장되는 방식이 얼핏 보기에는 실제의 값을 단순히 binary 로 바꾼 값처럼 보이지만,

이 binary 값은 기능적으로 입력되어 있을 뿐, 실존하는 값과는 다른 값 입니다.


결국, 입출력은 decimal, 저장된 것은 binary, 더욱이 이 decimal / binary 가 실제의 값을 표현하지는 않다는 것이죠.


그래서 값 치환이 3번 일어나게 됩니다.

- decimal 실제 값을 EEPROM 에 저장될 binary 를 상정하여 변경 (변경 1)

- 이를 decimal 로 치환하여 EERPOM 에 전송 (변경 2)

- EEPROM 에 보내면 알아서 다시 binary 의 나열로 저장 (변경 3)


값의 format 변경은, decimal --> binary --> decimal --> binary 로 되겠죠.


왜이리 복잡하게 하는가를 생각해 보면,

EEPROM 의 address 는 직접 접근 가능하지만, 각 address 의 bit 값은 따로따로 접근을 못하며,

거기에 각 bit 가 기능적으로 정의되어 있어서, "저장된 값 = 실존하는 값" 공식이 성립하지 않기 때문 입니다.



그럼 실제 source code 에서 10의 자리와 1의 자리의 연산식이 이떻게 이루어 지는지를 확인해 보죠.

초 단위의 식은 아래와 같습니다.


seconds = (((seconds & B11110000)>>4)*10 + (seconds & B00001111)); // convert BCD to decimal


상위 bit 부분 - 10의 자리 수 - "((seconds & B11110000)>>4)*10" 부분만을 띄어서 보면 다음과 같은 항목이 됩니다.

왼쪽부터,

- EEPROM 에서 읽어온 값 (decimal) 인 seconds

- seconds 값을 AND 연산 하면서 자동으로 binary 로 변환

- 4 bit 를 왼쪽으로 shift (날림) 를 통해 10의 자리만을 건짐

- 10 을 곱하면서 decimal 로 변경


위의 과정의 경과를 살펴보면 아래와 같겠죠.


| seconds | (BIN)seconds | seconds & B11110000 | (BIN)(seconds & B11110000) | (seconds & B11110000)>>4 | *10 |


0 | 0 | 0 | 0 | 0 | 0
1 | 1 | 0 | 0 | 0 | 0
2 | 10 | 0 | 0 | 0 | 0
3 | 11 | 0 | 0 | 0 | 0
4 | 100 | 0 | 0 | 0 | 0
5 | 101 | 0 | 0 | 0 | 0
6 | 110 | 0 | 0 | 0 | 0
7 | 111 | 0 | 0 | 0 | 0
8 | 1000 | 0 | 0 | 0 | 0
9 | 1001 | 0 | 0 | 0 | 0
16 | 10000 | 16 | 10000 | 1 | 10
17 | 10001 | 16 | 10000 | 1 | 10
18 | 10010 | 16 | 10000 | 1 | 10
19 | 10011 | 16 | 10000 | 1 | 10
21 | 10101 | 16 | 10000 | 1 | 10
22 | 10110 | 16 | 10000 | 1 | 10
23 | 10111 | 16 | 10000 | 1 | 10
24 | 11000 | 16 | 10000 | 1 | 10
25 | 11001 | 16 | 10000 | 1 | 10
32 | 100000 | 32 | 100000 | 2 | 20
33 | 100001 | 32 | 100000 | 2 | 20
34 | 100010 | 32 | 100000 | 2 | 20
35 | 100011 | 32 | 100000 | 2 | 20
36 | 100100 | 32 | 100000 | 2 | 20
37 | 100101 | 32 | 100000 | 2 | 20
38 | 100110 | 32 | 100000 | 2 | 20
39 | 100111 | 32 | 100000 | 2 | 20
40 | 101000 | 32 | 100000 | 2 | 20
41 | 101001 | 32 | 100000 | 2 | 20
48 | 110000 | 48 | 110000 | 3 | 30
49 | 110001 | 48 | 110000 | 3 | 30
50 | 110010 | 48 | 110000 | 3 | 30
51 | 110011 | 48 | 110000 | 3 | 30
52 | 110100 | 48 | 110000 | 3 | 30
53 | 110101 | 48 | 110000 | 3 | 30
54 | 110110 | 48 | 110000 | 3 | 30
55 | 110111 | 48 | 110000 | 3 | 30
56 | 111000 | 48 | 110000 | 3 | 30
57 | 111001 | 48 | 110000 | 3 | 30
64 | 1000000 | 64 | 1000000 | 4 | 40
65 | 1000001 | 64 | 1000000 | 4 | 40
66 | 1000010 | 64 | 1000000 | 4 | 40
67 | 1000011 | 64 | 1000000 | 4 | 40
68 | 1000100 | 64 | 1000000 | 4 | 40
69 | 1000101 | 64 | 1000000 | 4 | 40
71 | 1000111 | 64 | 1000000 | 4 | 40
72 | 1001000 | 64 | 1000000 | 4 | 40
73 | 1001001 | 64 | 1000000 | 4 | 40
80 | 1010000 | 80 | 1010000 | 5 | 50
81 | 1010001 | 80 | 1010000 | 5 | 50
82 | 1010010 | 80 | 1010000 | 5 | 50
83 | 1010011 | 80 | 1010000 | 5 | 50
84 | 1010100 | 80 | 1010000 | 5 | 50
85 | 1010101 | 80 | 1010000 | 5 | 50
86 | 1010110 | 80 | 1010000 | 5 | 50
87 | 1010111 | 80 | 1010000 | 5 | 50
88 | 1011000 | 80 | 1010000 | 5 | 50
89 | 1011001 | 80 | 1010000 | 5 | 50


결과적으로 10 --> 20 --> 30 --> 40 --> 50 순으로 값을 변경한다는 것을 알 수 있습니다.

보고 있으면 오묘하죠? 숫자놀이의 향연이라고 할 수 있겠습니다.






5. 알람 설정


아래는 EEPROM 에 저장된 기능별 주소록 입니다.


- datasheet : DS3231.pdf



저 위의 표를 염두에 두면서 DS3231.h 파일을 열어보면 알람의 설정방법에 대해 기술되어 있습니다.


/* Retrieves everything you could want to know about alarm
 * one. 
 * A1Dy true makes the alarm go on A1Day = Day of Week,
 * A1Dy false makes the alarm go on A1Day = Date of month.
 *
 * byte AlarmBits sets the behavior of the alarms:
 *	Dy	A1M4	A1M3	A1M2	A1M1	Rate
 *	X	1		1		1		1		Once per second
 *	X	1		1		1		0		Alarm when seconds match
 *	X	1		1		0		0		Alarm when min, sec match
 *	X	1		0		0		0		Alarm when hour, min, sec match
 *	0	0		0		0		0		Alarm when date, h, m, s match
 *	1	0		0		0		0		Alarm when DoW, h, m, s match
 *
 *	Dy	A2M4	A2M3	A2M2	Rate
 *	X	1		1		1		Once per minute (at seconds = 00)
 *	X	1		1		0		Alarm when minutes match
 *	X	1		0		0		Alarm when hours and minutes match
 *	0	0		0		0		Alarm when date, hour, min match
 *	1	0		0		0		Alarm when DoW, hour, min match
 */


자세히 보면, Alarm 1 이 초단위까지 자세하게 설정할 수 있으며,

Alarm 2 는 최소단위가 분단위임을 알 수 있습니다.

이 차이는 Alarm 1에 5개의 address 가 할당되어 있고, Alarm 2 에 4개의 address 가 할당되어 있는게 그 차이 입니다.


"DS3231.h" 에는 setA1Time, setA2TimeturnOnAlarm() 펑션이 있습니다.


File > Examples > DS3231 > DS3231_set 의 일부분이 다음과 같습니다.


		// Test of alarm functions
		// set A1 to one minute past the time we just set the clock
		// on current day of week.
		Clock.setA1Time(DoW, Hour, Minute+1, Second, 0x0, true, false, false);
		// set A2 to two minutes past, on current day of month.
		Clock.setA2Time(Date, Hour, Minute+2, 0x0, false, false, false);
		// Turn on both alarms, with external interrupt
		Clock.turnOnAlarm(1);
		Clock.turnOnAlarm(2);


이걸 이용하여 알람을 설정할 수 있습니다.

위의 source 는 특정 시간 설정 후, 1분과 2분 경과 후에 알람이 뜨도록 되어 있네요.


알람 확인은 기본으로 제공되는 다음 소스에서 확인할 수 있습니다.


File > Examples > DS3231 > DS3231_test


print 부분의 소스가 좀 부실해서 아주 쬐끔 수정 후, 표시된 내용이 아래 입니다.


2018 11 19 2 15 56 58 24h T=24.25 O+
Alarm 1: 2 DoW 15 57 0 enabled
Alarm_1 bits: 0
Alarm 2: 19 Date 15 58 enabled
Alarm_2 bits: 0

2018 11 19 2 15 56 59 24h T=24.25 O+
Alarm 1: 2 DoW 15 57 0 enabled
Alarm_1 bits: 0
Alarm 2: 19 Date 15 58 enabled
Alarm_2 bits: 0

2018 11 19 2 15 57 0 24h T=24.25 O+ A1!
Alarm 1: 2 DoW 15 57 0 enabled
Alarm_1 bits: 0
Alarm 2: 19 Date 15 58 enabled
Alarm_2 bits: 0

2018 11 19 2 15 57 2 24h T=24.25 O+
Alarm 1: 2 DoW 15 57 0 enabled
Alarm_1 bits: 0
Alarm 2: 19 Date 15 58 enabled
Alarm_2 bits: 0

......

2018 11 19 2 15 57 59 24h T=24.25 O+
Alarm 1: 2 DoW 15 57 0 enabled
Alarm_1 bits: 0
Alarm 2: 19 Date 15 58 enabled
Alarm_2 bits: 0

2018 11 19 2 15 58 0 24h T=24.25 O+ A2!
Alarm 1: 2 DoW 15 57 0 enabled
Alarm_1 bits: 0
Alarm 2: 19 Date 15 58 enabled
Alarm_2 bits: 0

2018 11 19 2 15 58 1 24h T=24.25 O+
Alarm 1: 2 DoW 15 57 0 enabled
Alarm_1 bits: 0
Alarm 2: 19 Date 15 58 enabled
Alarm_2 bits: 0


시간 설정 1분 후와 2분 후에 A1 과 A2 알람이 표시된 것을 확인할 수 있습니다.


알람 설정은, 시간 설정/표시 보다 훨씬 복잡합니다.

BCD 및 EEPROM 값에 대한 완벽한 이해가 있어야 하더군요.


소스를 새로 짤 수 있으나 너무 힘을 들이는 듯 해서,

지금까지 본것 중에 최고의 source 를 만들어 놓은 분의 website를 대신해서 기록해 봅니다.


* Arduino real time clock with alarm and temperature monitor using DS3231

https://simple-circuit.com/arduino-ds3231-real-time-clock-alarm-temperature/


이걸 만든 양반은 실제 상용으로 사용해도 될만큼 시간설정 및 알람 설정이 가능하도록 만들었습니다.

EEPROM 의 각 값들이 어떻게 사용되는지에 대한 완벽한 code 가 포함되어 있습니다.


또한 편한 library 를 사용하지 않고, 오로지 Wire.h 라이브러리만을 이용하여 직접 모든 것을 컨트롤 했습니다.


/* Arduino real time clock and calendar with 2 alarm functions and temperature monitor using DS3231
   Read DS3231 RTC datasheet to understand the code
   Time & date parameters can be set using two push buttons connected to pins 9 (B1) & 10 (B2).
   Alarm1 and alarm2 can be set using two push buttons connected to 11 (B3) & 10 (B2).
   Pin 12 becomes high when alarm occurred and button B2 returns it to low and
   turns the occurred alarm OFF.
   DS3231 interrupt pin is connected to Arduino external interrupt pin 2.
*/

// include LCD library code
#include "LiquidCrystal.h"
// include Wire library code (needed for I2C protocol devices)
#include "Wire.h"

// LCD module connections (RS, E, D4, D5, D6, D7)
LiquidCrystal lcd(3, 4, 5, 6, 7, 8);

const int button1   =  9;                   // button1 pin number
const int button2   = 10;                   // button1 pin number
const int button3   = 11;                   // button1 pin number
const int alarm_pin = 12;                   // Alarms pin number

void setup() {
  pinMode(9,  INPUT_PULLUP);
  pinMode(10, INPUT_PULLUP);
  pinMode(11, INPUT_PULLUP);
  pinMode(12, OUTPUT);
  digitalWrite(alarm_pin, LOW);
  // set up the LCD's number of columns and rows
  lcd.begin(20, 4);
  Wire.begin();                                 // Join i2c bus
  attachInterrupt(digitalPinToInterrupt(2), Alarm, FALLING);
}

// Variables declaration
bool alarm1_status, alarm2_status;
char Time[]     = "  :  :  ",
     calendar[] = "      /  /20  ",
     alarm1[]   = "A1:   :  :00", alarm2[]   = "A2:   :  :00",
     temperature[] = "T:   .   C";
byte  i, second, minute, hour, day, date, month, year,
      alarm1_minute, alarm1_hour, alarm2_minute, alarm2_hour,
      status_reg;

void Alarm(){
  digitalWrite(alarm_pin, HIGH);
}
void DS3231_read(){                             // Function to read time & calendar data
  Wire.beginTransmission(0x68);                 // Start I2C protocol with DS3231 address
  Wire.write(0);                                // Send register address
  Wire.endTransmission(false);                  // I2C restart
  Wire.requestFrom(0x68, 7);                    // Request 7 bytes from DS3231 and release I2C bus at end of reading
  second = Wire.read();                         // Read seconds from register 0
  minute = Wire.read();                         // Read minuts from register 1
  hour   = Wire.read();                         // Read hour from register 2
  day    = Wire.read();                         // Read day from register 3
  date   = Wire.read();                         // Read date from register 4
  month  = Wire.read();                         // Read month from register 5
  year   = Wire.read();                         // Read year from register 6
}
void alarms_read_display(){                     // Function to read and display alarm1, alarm2 and temperature data
  byte control_reg, temperature_lsb;
  char temperature_msb;
  Wire.beginTransmission(0x68);                 // Start I2C protocol with DS3231 address
  Wire.write(0x08);                             // Send register address
  Wire.endTransmission(false);                  // I2C restart
  Wire.requestFrom(0x68, 11);                   // Request 11 bytes from DS3231 and release I2C bus at end of reading
  alarm1_minute = Wire.read();                  // Read alarm1 minutes
  alarm1_hour   = Wire.read();                  // Read alarm1 hours
  Wire.read();                                  // Skip alarm1 day/date register
  alarm2_minute = Wire.read();                  // Read alarm2 minutes
  alarm2_hour   = Wire.read();                  // Read alarm2 hours
  Wire.read();                                  // Skip alarm2 day/date register
  control_reg = Wire.read();                    // Read the DS3231 control register
  status_reg  = Wire.read();                    // Read the DS3231 status register
  Wire.read();                                  // Skip aging offset register
  temperature_msb = Wire.read();                // Read temperature MSB
  temperature_lsb = Wire.read();                // Read temperature LSB
    // Convert BCD to decimal
  alarm1_minute = (alarm1_minute >> 4) * 10 + (alarm1_minute & 0x0F);
  alarm1_hour   = (alarm1_hour   >> 4) * 10 + (alarm1_hour & 0x0F);
  alarm2_minute = (alarm2_minute >> 4) * 10 + (alarm2_minute & 0x0F);
  alarm2_hour   = (alarm2_hour   >> 4) * 10 + (alarm2_hour & 0x0F);
    // End conversion
  alarm1[8]     = alarm1_minute % 10  + 48;
  alarm1[7]     = alarm1_minute / 10  + 48;
  alarm1[5]     = alarm1_hour   % 10  + 48;
  alarm1[4]     = alarm1_hour   / 10  + 48;
  alarm2[8]     = alarm2_minute % 10  + 48;
  alarm2[7]     = alarm2_minute / 10  + 48;
  alarm2[5]     = alarm2_hour   % 10  + 48;
  alarm2[4]     = alarm2_hour   / 10  + 48;
  alarm1_status = bitRead(control_reg, 0);      // Read alarm1 interrupt enable bit (A1IE) from DS3231 control register
  alarm2_status = bitRead(control_reg, 1);      // Read alarm2 interrupt enable bit (A2IE) from DS3231 control register
  if(temperature_msb < 0){
    temperature_msb = abs(temperature_msb);
    temperature[2] = '-';
  }
  else
    temperature[2] = ' ';
  temperature_lsb >>= 6;
  temperature[4] = temperature_msb % 10  + 48;
  temperature[3] = temperature_msb / 10  + 48;
  if(temperature_lsb == 0 || temperature_lsb == 2){
    temperature[7] = '0';
    if(temperature_lsb == 0) temperature[6] = '0';
    else                     temperature[6] = '5';
  }
  if(temperature_lsb == 1 || temperature_lsb == 3){
    temperature[7] = '5';
    if(temperature_lsb == 1) temperature[6] = '2';
    else                     temperature[6] = '7';
  }
  temperature[8]  = 223;                        // Put the degree symbol
  lcd.setCursor(10, 0);
  lcd.print(temperature);                       // Display temperature
  lcd.setCursor(0, 2);
  lcd.print(alarm1);                            // Display alarm1
  lcd.setCursor(17, 2);
  if(alarm1_status)  lcd.print("ON ");          // If A1IE = 1 print 'ON'
  else               lcd.print("OFF");          // If A1IE = 0 print 'OFF'
  lcd.setCursor(0, 3);
  lcd.print(alarm2);                            // Display alarm2
  lcd.setCursor(17, 3);
  if(alarm2_status)  lcd.print("ON ");          // If A2IE = 1 print 'ON'
  else               lcd.print("OFF");          // If A2IE = 0 print 'OFF'
}
void calendar_display(){                        // Function to display calendar
  switch(day){
    case 1:  strcpy(calendar, "Sun   /  /20  "); break;
    case 2:  strcpy(calendar, "Mon   /  /20  "); break;
    case 3:  strcpy(calendar, "Tue   /  /20  "); break;
    case 4:  strcpy(calendar, "Wed   /  /20  "); break;
    case 5:  strcpy(calendar, "Thu   /  /20  "); break;
    case 6:  strcpy(calendar, "Fri   /  /20  "); break;
    case 7:  strcpy(calendar, "Sat   /  /20  "); break;
    default: strcpy(calendar, "Sat   /  /20  ");
  }
  calendar[13] = year  % 10 + 48;
  calendar[12] = year  / 10 + 48;
  calendar[8]  = month % 10 + 48;
  calendar[7]  = month / 10 + 48;
  calendar[5]  = date  % 10 + 48;
  calendar[4]  = date  / 10 + 48;
  lcd.setCursor(0, 1);
  lcd.print(calendar);                          // Display calendar
}
void DS3231_display(){
  // Convert BCD to decimal
  second = (second >> 4) * 10 + (second & 0x0F);
  minute = (minute >> 4) * 10 + (minute & 0x0F);
  hour = (hour >> 4) * 10 + (hour & 0x0F);
  date = (date >> 4) * 10 + (date & 0x0F);
  month = (month >> 4) * 10 + (month & 0x0F);
  year = (year >> 4) * 10 + (year & 0x0F);
  // End conversion
  Time[7]     = second % 10  + 48;
  Time[6]     = second / 10  + 48;
  Time[4]     = minute % 10  + 48;
  Time[3]     = minute / 10  + 48;
  Time[1]     = hour   % 10  + 48;
  Time[0]     = hour   / 10  + 48;
  calendar_display();                           // Call calendar display function
  lcd.setCursor(0, 0);
  lcd.print(Time);                              // Display time
}
void Blink(){
  byte j = 0;
  while(j < 10 && (digitalRead(button1) || i >= 5) && digitalRead(button2) && (digitalRead(button3) || i < 5)){
    j++;
    delay(25);
  }
}
byte edit(byte x, byte y, byte parameter){
  char text[3];
  while(!digitalRead(button1) || !digitalRead(button3));    // Wait until button B1 is released
  while(true){
    while(!digitalRead(button2)){                           // If button B2 is pressed
      parameter++;
      if(((i == 0) || (i == 5)) && parameter > 23)          // If hours > 23 ==> hours = 0
        parameter = 0;
      if(((i == 1) || (i == 6)) && parameter > 59)          // If minutes > 59 ==> minutes = 0
        parameter = 0;
      if(i == 2 && parameter > 31)                          // If date > 31 ==> date = 1
        parameter = 1;
      if(i == 3 && parameter > 12)                          // If month > 12 ==> month = 1
        parameter = 1;
      if(i == 4 && parameter > 99)                          // If year > 99 ==> year = 0
        parameter = 0;
      if(i == 7 && parameter > 1)                           // For alarms ON or OFF (1: alarm ON, 0: alarm OFF)
        parameter = 0;
      lcd.setCursor(x, y);
      if(i == 7){                                           // For alarms ON & OFF
        if(parameter == 1)  lcd.print("ON ");
        else                lcd.print("OFF");
      }
      else{
        sprintf(text,"%02u", parameter);
        lcd.print(text);
      }
      if(i >= 5){
        DS3231_read();                          // Read data from DS3231
        DS3231_display();                       // Display DS3231 time and calendar
      }
      delay(200);                               // Wait 200ms
    }
    lcd.setCursor(x, y);
    lcd.print("  ");                            // Print two spaces
    if(i == 7) lcd.print(" ");                  // Print space (for alarms ON & OFF)
    Blink();                                    // Call Blink function
    lcd.setCursor(x, y);
    if(i == 7){                                 // For alarms ON & OFF
      if(parameter == 1)  lcd.print("ON ");
      else                lcd.print("OFF");
    }
    else{
      sprintf(text,"%02u", parameter);
      lcd.print(text);
    }
    Blink();
    if(i >= 5){
      DS3231_read();
      DS3231_display();}
    if((!digitalRead(button1) && i < 5) || (!digitalRead(button3) && i >= 5)){
      i++;                                      // Increment 'i' for the next parameter
      return parameter;                         // Return parameter value and exit
    }
  }
}

void loop() {
  if(!digitalRead(button1)){                    // If B1 button is pressed
      i = 0;
      hour   = edit(0, 0, hour);
      minute = edit(3, 0, minute);
      while(!digitalRead(button1));             // Wait until button B1 released
      while(true){
        while(!digitalRead(button2)){           // If button B2 button is pressed
          day++;                                // Increment day
          if(day > 7) day = 1;
          calendar_display();                   // Call display_calendar function
          lcd.setCursor(0, 1);
          lcd.print(calendar);                  // Display calendar
          delay(200);
        }
        lcd.setCursor(0, 1);
        lcd.print("   ");                       // Print 3 spaces
        Blink();
        lcd.setCursor(0, 1);
        lcd.print(calendar);                    // Print calendar
        Blink();                                // Call Blink function
        if(!digitalRead(button1))               // If button B1 is pressed
          break;
      }
      date = edit(4, 1, date);                  // Edit date
      month = edit(7, 1, month);                // Edit month
      year = edit(12, 1, year);                 // Edit year
      // Convert decimal to BCD
      minute = ((minute / 10) << 4) + (minute % 10);
      hour = ((hour / 10) << 4) + (hour % 10);
      date = ((date / 10) << 4) + (date % 10);
      month = ((month / 10) << 4) + (month % 10);
      year = ((year / 10) << 4) + (year % 10);
      // End conversion
      // Write time & calendar data to DS3231 RTC
      Wire.beginTransmission(0x68);             // Start I2C protocol with DS3231 address
      Wire.write(0);                            // Send register address
      Wire.write(0);                            // Reset sesonds and start oscillator
      Wire.write(minute);                       // Write minute
      Wire.write(hour);                         // Write hour
      Wire.write(day);                          // Write day
      Wire.write(date);                         // Write date
      Wire.write(month);                        // Write month
      Wire.write(year);                         // Write year
      Wire.endTransmission();                   // Stop transmission and release the I2C bus
      delay(200);
    }
    if(!digitalRead(button3)){                  // If B3 button is pressed
      while(!digitalRead(button3));             // Wait until button B3 released
      i = 5;
      alarm1_hour   = edit(4,  2, alarm1_hour);
      alarm1_minute = edit(7,  2, alarm1_minute);
      alarm1_status = edit(17, 2, alarm1_status);
      i = 5;
      alarm2_hour   = edit(4,  3, alarm2_hour);
      alarm2_minute = edit(7,  3, alarm2_minute);
      alarm2_status = edit(17, 3, alarm2_status);
      alarm1_minute = ((alarm1_minute / 10) << 4) + (alarm1_minute % 10);
      alarm1_hour   = ((alarm1_hour   / 10) << 4) + (alarm1_hour % 10);
      alarm2_minute = ((alarm2_minute / 10) << 4) + (alarm2_minute % 10);
      alarm2_hour   = ((alarm2_hour   / 10) << 4) + (alarm2_hour % 10);
      // Write alarms data to DS3231
      Wire.beginTransmission(0x68);               // Start I2C protocol with DS3231 address
      Wire.write(7);                              // Send register address (alarm1 seconds)
      Wire.write(0);                              // Write 0 to alarm1 seconds
      Wire.write(alarm1_minute);                  // Write alarm1 minutes value to DS3231
      Wire.write(alarm1_hour);                    // Write alarm1 hours value to DS3231
      Wire.write(0x80);                           // Alarm1 when hours, minutes, and seconds match
      Wire.write(alarm2_minute);                  // Write alarm2 minutes value to DS3231
      Wire.write(alarm2_hour);                    // Write alarm2 hours value to DS3231
      Wire.write(0x80);                           // Alarm2 when hours and minutes match
      Wire.write(4 | alarm1_status | (alarm2_status << 1));      // Write data to DS3231 control register (enable interrupt when alarm)
      Wire.write(0);                              // Clear alarm flag bits
      Wire.endTransmission();                     // Stop transmission and release the I2C bus
      delay(200);                                 // Wait 200ms
    }
    if(!digitalRead(button2) && digitalRead(alarm_pin)){         // When button B2 pressed with alarm (Reset and turn OFF the alarm)
      digitalWrite(alarm_pin, LOW);               // Turn OFF the alarm indicator
      Wire.beginTransmission(0x68);               // Start I2C protocol with DS3231 address
      Wire.write(0x0E);                           // Send register address (control register)
      // Write data to control register (Turn OFF the occurred alarm and keep the other as it is)
      Wire.write(4 | (!bitRead(status_reg, 0) & alarm1_status) | ((!bitRead(status_reg, 1) & alarm2_status) << 1));
      Wire.write(0);                              // Clear alarm flag bits
      Wire.endTransmission();                     // Stop transmission and release the I2C bus
    }
    DS3231_read();                                // Read time and calendar parameters from DS3231 RTC
    alarms_read_display();                        // Read and display alarms parameters
    DS3231_display();                             // Display time & calendar
    delay(50);                                    // Wait 50ms
}
// End of code


이렇게 긴 source 는 붙여넣기 하면 이쁘지 않지만, 너무 잘 짠 코드라 여기에 남깁니다.





6. Square Wave - 정현파 만들기


RTC에 왠 정현파 재조기 인가 하는데, 시간은 정확한 oscillator 를 사용하므로,

square wave 를 정확하게 만들 수 있는 기능이 있습니다.


만들 수 있는 주파수는 다음과 같습니다.

- 1.024kHz

- 4.096kHz

- 8.192kHz

- 32.768kHz


source 는 Example 에 올라와 있는 내용을 그대로 사용해 봤습니다.


File > Examples > DS3231 > DS3231_oscillator_test


		// Oscillator functions

		void enableOscillator(bool TF, bool battery, byte frequency); 
			// turns oscillator on or off. True is on, false is off.
			// if battery is true, turns on even for battery-only operation,
			// otherwise turns off if Vcc is off.
			// frequency must be 0, 1, 2, or 3.
			// 0 = 1 Hz
			// 1 = 1.024 kHz
			// 2 = 4.096 kHz
			// 3 = 8.192 kHz (Default if frequency byte is out of range);
		void enable32kHz(bool TF); 
			// Turns the 32kHz output pin on (true); or off (false).
		bool oscillatorCheck();;
			// Checks the status of the OSF (Oscillator Stop Flag);.
			// If this returns false, then the clock is probably not
			// giving you the correct time.
			// The OSF is cleared by function setSecond();.


마침 DSO150 이라는 DIY oscilloscope 를 만들어 놓은 것이 있네요.


* Hardware | DSO150 Oscilloscope

http://chocoball.tistory.com/entry/HardwareDSO150Oscilloscope


SQW 에 DSO150 을 연결해서 확인해 봅니다.

신기하게도 정현파가 잘 뜨네요.



단, 32kHz 는 높은 주파수라서 그런지 32K 단자에서 따로 검출할 수 있습니다.






7. 시간 표시를 2 digit 로 바꾸기


시간, 분, 초의 표시가 10자리 밑이면 한자리 값으로만 나옵니다.

6초면 "06"이 아니라 "6" 으로 표시되면서 자리 위치가 자꾸 바뀌는게 마음에 들지 않습니다.



값을 EEPROM 에서 리턴 받으면 10 이하일 경우 "0" 을 삽입해 주는 코드를 추가하여 아래와 같이 수정했습니다.


#include "SPI.h"
#include "Wire.h"
#include "Adafruit_GFX.h"
#include "Adafruit_SSD1306.h"
 
Adafruit_SSD1306 display = Adafruit_SSD1306();
  
#define DS3231_I2C_ADDRESS 104
 
// SCL - pin A5
// SDA - pin A4
// To set the clock, run the sketch and use the serial monitor.
// Enter T1124154091014; the code will read this and set the clock. See the code for full details.
 
byte seconds, minutes, hours, day, date, month, year;
char weekDay[4];
 
byte tMSB, tLSB;
float temp3231;

String T_seconds, T_minutes, T_hours, D_date, D_month, D_year;
 
void setup() {
  Serial.begin(9600);
   
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3C (for the 128x32)
  display.clearDisplay();
  display.display();
  delay(1000);
 
  Wire.begin();
}
 
void loop() {
  watchConsole();
   
  get3231Date();
   
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setCursor(0,0);
  display.setTextSize(1);

  if (seconds < 10) { T_seconds = "0" + String(seconds, DEC);} else {T_seconds = String(seconds, DEC);}
  if (minutes < 10) { T_minutes = "0" + String(minutes, DEC);} else {T_minutes = String(minutes, DEC);}
  if (hours < 10) { T_hours = "0" + String(hours, DEC);} else {T_hours = String(hours, DEC);}
  if (date < 10) { D_date = "0" + String(date, DEC);} else {D_date = String(date, DEC);}
  if (month < 10) { D_month = "0" + String(month, DEC);} else {D_month = String(month, DEC);}
  if (year < 10) { D_year = "0" + String(year, DEC);} else {D_year = String(year, DEC);}
  
  display.print("DATE : "); display.print(weekDay); display.print(", "); display.print(D_date); display.print("/"); display.print(D_month); display.print("/"); display.println(D_year);
  display.print("TIME : "); display.print(T_hours); display.print(":"); display.print(T_minutes); display.print(":"); display.println(T_seconds);
  display.print("TEMP : "); display.println(get3231Temp());
  display.display();
   
  delay(1000);
}
 
// Convert normal decimal numbers to binary coded decimal
byte decToBcd(byte val) {
  return ( (val/10*16) + (val%10) );
}
 
void watchConsole() {
   
  if (Serial.available()) { // Look for char in serial queue and process if found
    if (Serial.read() == 84) { //If command = "T" Set Date
      set3231Date();
      get3231Date();
      Serial.println(" ");
    }
  }
}
  
void set3231Date() {
  //T(sec)(min)(hour)(dayOfWeek)(dayOfMonth)(month)(year)
  //T(00-59)(00-59)(00-23)(1-7)(01-31)(01-12)(00-99)
  //Example: 02-Feb-09 @ 19:57:11 for the 3rd day of the week -> T1157193020209
  // T1124154091014
  seconds = (byte) ((Serial.read() - 48) * 10 + (Serial.read() - 48)); // Use of (byte) type casting and ascii math to achieve result.
  minutes = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));
  hours   = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));
  day     = (byte) (Serial.read() - 48); // set day of week (1=Sunday, 7=Saturday)
  date    = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));
  month   = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));
  year    = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));
   
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0x00);
  Wire.write(decToBcd(seconds));
  Wire.write(decToBcd(minutes));
  Wire.write(decToBcd(hours));
  Wire.write(decToBcd(day));
  Wire.write(decToBcd(date));
  Wire.write(decToBcd(month));
  Wire.write(decToBcd(year));
  Wire.endTransmission();
}
 
void get3231Date() {
  // send request to receive data starting at register 0
  Wire.beginTransmission(DS3231_I2C_ADDRESS); // 104 is DS3231 device address
  Wire.write(0x00); // start at register 0
  Wire.endTransmission();
  Wire.requestFrom(DS3231_I2C_ADDRESS, 7); // request seven bytes
   
  if(Wire.available()) {
    seconds = Wire.read(); // get seconds
    minutes = Wire.read(); // get minutes
    hours   = Wire.read(); // get hours
    day     = Wire.read();
    date    = Wire.read();
    month   = Wire.read(); //temp month
    year    = Wire.read();

    seconds = (((seconds & B11110000)>>4)*10 + (seconds & B00001111)); // convert BCD to decimal
    minutes = (((minutes & B11110000)>>4)*10 + (minutes & B00001111)); // convert BCD to decimal
    hours   = (((hours & B00110000)>>4)*10 + (hours & B00001111)); // convert BCD to decimal (assume 24 hour mode)
    day     = (day & B00000111); // 1-7
    date    = (((date & B00110000)>>4)*10 + (date & B00001111)); // 1-31
    month   = (((month & B00010000)>>4)*10 + (month & B00001111)); //msb7 is century overflow
    year    = (((year & B11110000)>>4)*10 + (year & B00001111));
  } else {
    //oh noes, no data!
  }
   
  switch (day) {
    case 1:
      strcpy(weekDay, "Sun");
      break;
    case 2:
      strcpy(weekDay, "Mon");
      break;
    case 3:
      strcpy(weekDay, "Tue");
      break;
    case 4:
      strcpy(weekDay, "Wed");
      break;
    case 5:
      strcpy(weekDay, "Thu");
      break;
    case 6:
      strcpy(weekDay, "Fri");
      break;
    case 7:
      strcpy(weekDay, "Sat");
      break;
  }
}
 
float get3231Temp() {
  //temp registers (11h-12h) get updated automatically every 64s
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0x11);
  Wire.endTransmission();
  Wire.requestFrom(DS3231_I2C_ADDRESS, 2);
   
  if(Wire.available()) {
    tMSB = Wire.read(); //2's complement int portion
    tLSB = Wire.read(); //fraction portion
     
    temp3231 = (tMSB & B01111111); //do 2's math on Tmsb
    temp3231 += ( (tLSB >> 6) * 0.25 ); //only care about bits 7 & 8
  } else {
    //oh noes, no data!
  }
  return temp3231;
}


10보다 작은 수가 오면 자동으로 "0" 을 붙여주게 되었습니다.



요일 값을 year / month / date 값을 이용하여 자동으로 연산하여 넣어줄 수 있도록 할 수도 있습니다만,

너무 복잡해 지므로 관련해서 코드를 짠 분의 site 를 링크해 놓습니다.


* Day of the Week calculator

https://www.hackster.io/erikuchama/day-of-the-week-calculator-cde704






8. DS3231 내부 온도 센서와 BME280 센서와 비교


예전에 온도 전용 센서인 BME280 을 이용하여 측정해 본 경험이 있습니다.


* Hardware | BME280 sensor

http://chocoball.tistory.com/entry/HardwareBME280


DS3231 내부 온도센서의 정확성 비교를 위해 하룻저녁 두개를 같이 측정해 봤습니다.



꽤나 근접하네요.


DS3231 의 결과값에 일괄적으로 +0.25 를 했더니만 이제 좀 비슷해 진것 같습니다.

그 결과 그래프가 아래 그림입니다. 최종적으로는 +0.3 정도가 가장 적당해 보이네요.



DS3231 내부 온도센서의 정확성을 위해,

추출한 값을 100 곱한 다음, 마지막에 100 으로 나누는 방식을 채용하여 측정하였습니다.


* How to read DS3231 Internal temperature sensor, example code

http://forum.arduino.cc/index.php?topic=262986.15


아래 참고한 사이트를 보면 DS3231 을 가지고 온갖 할 수 있는 일을 다하는 사람의 글 입니다.


* Using a $1 DS3231 Real-time Clock Module with Arduino

https://thecavepearlproject.org/2014/05/21/using-a-cheap-3-ds3231-rtc-at24c32-eeprom-from-ebay/


최종 비교를 위해 사용된 sketch 는 다음과 같습니다.


#include "Wire.h"
#include "SPI.h"
#include "Adafruit_Sensor.h"
#include "Adafruit_BME280.h"

#define BME_SCK 13
#define BME_MISO 12
#define BME_MOSI 11
#define BME_CS 10

Adafruit_BME280 BME;
   
const int DS3231_RTC_ADDR = 0x68;
const int DS3231_TEMP_MSB = 0x11;
union int16_byte {
  int i;
  byte b[2];
} rtcTemp;

void setup() {
  Wire.begin();
  Serial.begin(9600);
  BME.begin();
}

void loop() {
   Wire.beginTransmission(DS3231_RTC_ADDR);
   Wire.write(DS3231_TEMP_MSB);
   Wire.endTransmission();
   Wire.requestFrom(DS3231_RTC_ADDR, 2);
   rtcTemp.b[1] = Wire.read(); 
   rtcTemp.b[0] = Wire.read();
   long tempC100 = (rtcTemp.i >> 6) * 25;    //degrees celsius times 100
   Serial.print( tempC100 / 100 );
   Serial.print( '.' );
   Serial.print( abs(tempC100 % 100) );
   
   Serial.print("\t");
   Serial.println(BME.readTemperature());

   delay(1000);
}





FIN


이번에 DS3231 을 가지고 놀면서 대학때 배운 BCD 도 다시 해보고,

EEPROM 에 대한 address 방식 등에 대해서도 배울 수 있어서 좋았습니다....

만, 배울게 너무 많아서 힘들었습니다.


당연히 지금껏 사용해본 sensor 중에는 활용도와 배울 점으로는 단연 top 입니다.

관련해서 전문가들도 온갖 기술을 구현해 놨고... 정말 변태같은 sensor 인듯 합니다.

(datasheet 를 보면 뭔가 더 많은데, 여기서 그만 하려구요.)


And

Hardware | AM2322 Temperature & Humidity Sensor

|

1. 온도 센서

온도를 측정할 수 있는 센서는 매우 다양하게 있습니다.

아래 링크들은 지금까지 실험해본 센서들 입니다.


* Hardware | Arduino BMP280 고도/온도/기압 센서

http://chocoball.tistory.com/entry/HardwareArduinoBMP280


* Hardware | BME280 sensor

http://chocoball.tistory.com/entry/HardwareBME280


* Hardware | Arduino 비접촉 온도센서 GY-906 MLX90614

http://chocoball.tistory.com/entry/HardwareArduinoMLX90614



생각보다 온도 센서들이 다양하게 있습니다.

그러던 중 SHT 계얄과 AM 계열이 또 있다는 것을 알게 되었죠.


그럼 어떤게 더 좋을까 찾아 봤습니다.

정확면에서는 AM 계열이 좋다는 글을 어디선가 보았습니다.


각 온도 센서들을 비교한 사이트도 있습니다만, AM 계열은 다루기가 까다롭다는 것을 알 수 있었습니다.

(측정을 위해 연속 측정하지 않고, reset 해야 하는 등)


http://3. - https://www.kandrsmith.org/RJS/Misc/Hygrometers/calib_many.html


그렇다면, 일단 AM2321 을 구입하기로 하고 AliExpress 를 뒤져 봅니다.


옷!!!

새로운 버전인 AM2322가 나와 있네요.


https://ko.aliexpress.com/item/1pcs-AM2321-digital-temperature-and-humidity-sensors-can-replace-SHT21-SHT10-SHT11/32324748233.html



센서 계열에서 3 USD 이상이면 고가에 속합니다.

하지만 구입합니다. 당연히 배송은 무료 입니다.




2. 도착

약 한달 반 정도 걸린것 같습니다.

오래 기다렸네요.


받고 바로 드는 생각은, "작다" 였습니다.




보통 빵판의 핀 사이의 거리가 "2.54 mm" 인데, 이 센서의 다리 간격이 "1.27 mm" 입니다.

연결을 어떻게 해야할지 고민됩니다.


결국 adapter 를 살까 하다가, 주문하고 기다리는 것을 참을 수가 없어 다리를 구부리기로 합니다.

까딱 잘못하다가는 다리가 날라갈 수가 있습니다.


참고로 1.27 to 2.54 어뎁터는 아래 사이트에서 판매합니다. (거의 안파는 듯)


http://www.proto-advantage.com/store/product_info.php?products_id=2700037



향후를 위해서는 꼭 구하고 십지만, 7 USD 입니다. 배보다 배꼽이 더 큰 현실.




3. Spec.

스펙은 아래 datasheet 를 참고하세요.


AM2322_datasheet.pdf


이번 계기로, 중국어 공부에 대해서 심각하게 고민하게 되었습니다.


다행히 AM2320 에 대한 자료나 소스는 많아서, 비슷하다고 생각하고 진행해 봅니다.




4. Layout

Pin 배열은 다음과 같습니다.

판매자 사이트에 설명이 나와 있습니다.


  AM2322 | Arduino Nano
-------------------------
   Pin 1 |     3.3V
   Pin 2 |      A4
   Pin 3 |     GND
   Pin 4 |      A5
-------------------------



보드 구성은 다음과 같아요.



리드선의 피치가 작아서 아래처럼 할수 없이 다리들을 엇갈리게 구부렸습니다.




다리를 서로 간섭이 안생기도록 벌리고, 클립으로 리드선과 연결해 주었습니다.



최종 연결한 모습 입니다.

불안하게 연결되어 있어서 센서와의 통신도 자주 끊겨버리네요.


일단 동작 확인이 우선이므로, 정식으로 프로젝트 한다면 어뎁터가 꼭 있어야겠습니다.







5. Source

소스 코드는 아래 사이트를 참고하였습니다.


http://www.instructables.com/id/Connecting-AM2320-With-Arduino/


#include 
#include 
AM2320 th;

void setup() {
  Serial.begin(9600);
}

void loop() {
  switch(th.Read()) {
    case 2:
      Serial.println("CRC failed");
      break;
    case 1:
      Serial.println("Sensor offline");
      break;
    case 0:
      Serial.print("humidity: ");
      Serial.print(th.h);
      Serial.print("%, temperature: ");
      Serial.print(th.t);
      Serial.println("*C");
      break;
  }

  delay(200);
}


위의 사이트 말고도 여러 사이트를 참고하실 수 있습니다.


https://github.com/hibikiledo/AM2320

https://github.com/Ten04031977/AM2320-master




6. 결과

IDE 의 Sensor Monitor 를 통해 본 결과 입니다.



결과가 잘 나옵니다.

습도가 점점 올라가는 것은, 예상 하셨듯 입김을 불어 넣어준 결과 입니다. 잘 반응하네요.






FIN

어? 또다른 온도 센서가 하나 더 있네 ?!!!



And

Hardware | BME280 sensor

|

1. 시작하기

BMP280 을 구동시키면서 googling 해보면, "Humidity = 습도" 값을 표시해주는 글들이 심심치 않게 보였습니다.

그래서 열씸히 Humidity 를 표현하려고 이리저리 시도해 봤습니다.


또, Aliexpress 에서 구입한 BMP280 제품을 보면 GY-BME/P280 이라고 표시되어 있습니다.

할거 다 해봤습니다.


아니 그런뒈.... 그런뒈! (컬투 버전) 



2. 알아버렸다

그렇습니다. Humidity 를 하려면, 정확하게 BME280 이 있어야 합니다. BMP280 로는 안되는 것이였습니다.

아놔.




나를 깨우쳐준 글.



정말 되냐 안되냐를 확인할 수 있는 글.



3. 넌 누구냐

직접 확인해 봅니다.

SparkFun 이 제공하는 'I2C_ReadAllData.ino' sketch 를 통해서 0x58 이냐, 0x60 이냐를 확인해 봅니다.


SparkFun Library 를 아래 링크를 통해 다운받고 설치합니다.


 - https://github.com/sparkfun/SparkFun_BME280_Arduino_Library


마지막으로 I2C address 를 0x77 > 0x76 으로 바꾸고 실행해 봅니다.



아놔... 넌 BMP280 이구나...



4. BME280 구입

정말 정말 Humidity 수치를 알고 싶습니다.


한꺼번에 구입하지 못 한것을 탓하면서, 다시 주문을 넣습니다.

3주만에 오네요.



2개를 주문해서 앞뒤로 한 샷에 넣어 봅니다.



위의 부분이 센서군요. 보통 Bosch 에서 만든다고 하는데, Bosch 각인은 아닌것 같습니다.




이놈이 진짜 BME280 인지, 바로! 확인해 봅니닷!



오옷! 맞네요.
(정보를 알려준 외국인 친구 감사~!)



5. 구동해 보기

Library 를 다운받아 설치합니다.


 - https://github.com/adafruit/Adafruit_BME280_Library


I2C address 를 0x77 > 0x76 으로 꼭 변경해야 합니다.



이거 수정하지 않고 한참 해멨습니다.



이제 example sketch 를 로딩합니다.



소스는 다음과 같습니다.


/***************************************************************************
  This is a library for the BME280 humidity, temperature & pressure sensor

  Designed specifically to work with the Adafruit BME280 Breakout
  ----> http://www.adafruit.com/products/2650

  These sensors use I2C or SPI to communicate, 2 or 4 pins are required
  to interface. The device's I2C address is either 0x76 or 0x77.

  Adafruit invests time and resources providing this open source code,
  please support Adafruit andopen-source hardware by purchasing products
  from Adafruit!

  Written by Limor Fried & Kevin Townsend for Adafruit Industries.
  BSD license, all text above must be included in any redistribution
 ***************************************************************************/

#include 
#include 
#include 
#include 

#define BME_SCK 13
#define BME_MISO 12
#define BME_MOSI 11
#define BME_CS 10

#define SEALEVELPRESSURE_HPA (1013.25)

Adafruit_BME280 bme; // I2C
//Adafruit_BME280 bme(BME_CS); // hardware SPI
//Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); // software SPI

unsigned long delayTime;

void setup() {
    Serial.begin(9600);
    Serial.println(F("BME280 test"));

    bool status;
    
    // default settings
    status = bme.begin();
    if (!status) {
        Serial.println("Could not find a valid BME280 sensor, check wiring!");
        while (1);
    }
    
    Serial.println("-- Default Test --");
    delayTime = 1000;

    Serial.println();
}


void loop() { 
    printValues();
    delay(delayTime);
}


void printValues() {
    Serial.print("Temperature = ");
    Serial.print(bme.readTemperature());
    Serial.println(" *C");

    Serial.print("Pressure = ");

    Serial.print(bme.readPressure() / 100.0F);
    Serial.println(" hPa");

    Serial.print("Approx. Altitude = ");
    Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
    Serial.println(" m");

    Serial.print("Humidity = ");
    Serial.print(bme.readHumidity());
    Serial.println(" %");

    Serial.println();
}

layout 은 다음과 같고, pin 구성은 BMP280 과 같이 I2C 연결과 같습니다.


- VIN : 3.3V

- GND : GND

- SCL : A5

- SDA : A4



빵판에 연결합니다.




6. 결과

얏호~!!!

이제 Humidity 를 볼 수 있어요.



더 사용폭이 많은 BME280 만 만들지, 왜 BMP280 을 만드냐고 살짝 울분을 토해봅니다..



FIN

이제 뭘하지?

And
prev | 1 | next