'aliexpress'에 해당되는 글 152건
- 2018.12.02 Hardware | MAX30105 파티클 센서 - 2
- 2018.11.23 Hardware | MAX30105 파티클 센서 - 1
- 2018.11.20 Hardware | RTC DS3231 부품 사용기 - 2
- 2018.11.11 Hardware | RTC DS3231 부품 사용기 - 1 2
- 2018.11.10 Hardware | Transistor 구매
- 2018.11.09 Hardware | 스위치 부품 구매하기
- 2018.10.04 Hardware | ESP-01 or ESP8266 사용기 - 1 4
- 2018.08.12 Hardware | Safecast bGeigi nano firmware upgrade
- 2018.08.10 Hardware | Raspberry Pi 3 model B+ unboxing
- 2018.08.06 Hardware | MAX31865 + PT100 온도센서 조합 2
이 글은 이전 MAX30105 파티클 센서를 활용하여 OLED 에 심전도를 그려보는 도전기 입니다.
이전 글은 아래 link 를 참고하세요.
* Hardware | MAX30105 파티클 센서 - 1
- http://chocoball.tistory.com/entry/Hardware-MAX30105-particle-sensor-1
1. min, max 와 scalar
일단 MAX30105 에서 입력받는 값은 5만에서 많게는 10만도 넘어갑니다.
이를 32 pixel 너비를 갖는 OLED 에 표현하려면 그 값에 맞도록 축소시켜야 합니다.
이에 더하여, 값들의 기준이 아래 그림처럼 널을 뛰므로 기준값을 잡기가 여간 쉽지 않습니다.
기준값을 잡기 위해 min, max 값을 한 사이클당 판별하여 scale in 치환값을 잡아줘야 합니다.
이를 위해 일단, 이전 사이클에서 얻은 제일 작은 min 값을 이용하여,
입력받는 값에서 빼면 그래프가 아래 그림처럼 내려갑니다.
OLED 폭이 128 pixel 이므로, 이 폭을 한 사이클로 잡습니다.
EXCEL 을 통하여 위의 방식을 검증해 본 결과 문제 없이 표현되는 것을 확인할 수 있었습니다.
OLED 의 y 값에 해당하는 값을 EXCEL 에서 32 pixel 기준으로 변환된 값을 array 에 입력하고
순서대로 출력해서 OLED 에 맞는지 최종 확인해 봅니다.
요리조리 검증해 본 결과 아래 식을 통해서 min, max 값과 한 사이클을 OLED 폭인 128 에 표현하고
다시 0 부터 시작하는 - 화면이 바뀌는 방법을 확인하였습니다.
irValue = particleSensor.getIR(); if( x > 127) { //refresh OLED screen display.clearDisplay(); lastx=0; x=1; scaler = (max_irValue - min_irValue) / 32; pre_min_irValue = min_irValue; // reset min, max value max_irValue = irValue; min_irValue = irValue - 1; }
2. y값 reverse
OLED 에 표현시, x/y 값은 우리가 일반적으로 배운 왼쪽 아래에서 시작되는 것이 아니라,
왼쪽 위로부터 값의 증가를 표현합니다.
즉, sensor 로부터 값을 입력 받으면, 상하 전치를 시켜줘야 합니다.
제가 쓰고 있는 것은 상하 32 pixel 이므로, 아래 한 pixel 을 빼면 31 이므로,
31 에서 입력받은 값을 빼주면 표현하고자 하는 위치로 바꿀 수 있습니다.
// invert y values to fit OLED display if ( pre_min_irValue > irValue) { y = 31; } else { y = 31 - ((irValue - pre_min_irValue) / scaler); }
값이 너무 적어지면, 값이 역전하여 아래 그림같이 튀어버립니다.
그래서 전 사이클에서 확인 되었던 min value 보다 적어지면 강제적으로 최저값을 할당합니다.
여기서 "y = 31" 은 OLED 상에서는 제일 밑에 표시되는, 일상의 0 값과 같습니다.
3. BPM 과 piezo buzzer
심전도의 값이 가장 높아지면 buzzer 를 울리게 하는 소스 입니다.
또한 BPM 값도 표현해 줍니다.
BPM 값은 심전도의 한 사이클 평균을 내어 1분동안 얼마나 맥박이 뛰는지를 수치화 한 값입니다.
소스는 다음과 같고, 아래 blog 를 전면 참고하였습니다.
* Heart beat Sensor and “ECG” Display
- http://www.xtronical.com/basics/heart-beat-sensor-ecg-display/
ThisTime=millis(); if( y < UpperThreshold ) { if(BeatComplete) { BPM=ThisTime-LastTime; BPM=int(60/(float(BPM)/1000)); BPMTiming=false; BeatComplete=false; tone(2,1000,250); } if(BPMTiming==false) { LastTime=millis(); BPMTiming=true; } } if((y > LowerThreshold) && (BPMTiming)) BeatComplete=true;
4. source
지금까지의 작업을 모두 합하여 하나의 소스로 만들었습니다.
#include "Wire.h" #include "MAX30105.h" #include "SPI.h" #include "Adafruit_GFX.h" #include "Adafruit_SSD1306.h" // Hardware SPI #define OLED_DC 6 #define OLED_CS 7 #define OLED_RESET 8 Adafruit_SSD1306 display(OLED_DC, OLED_RESET, OLED_CS); MAX30105 particleSensor; unsigned int irValue; unsigned int max_irValue; unsigned int min_irValue; unsigned int x = 0; unsigned int y; unsigned int lastx = 0; unsigned int lasty = 0; unsigned int scaler; unsigned int pre_min_irValue; int BPM; int LastTime = 0; int ThisTime; bool BPMTiming=false; bool BeatComplete=false; #define UpperThreshold 10 #define LowerThreshold 25 void setup() { Wire.begin(); display.begin(SSD1306_SWITCHCAPVCC); display.clearDisplay(); // initialize sensor if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) { //Use default I2C port, 400kHz speed display.println("MAX30105 was not found. Please check wiring/power."); while (1); } //Setup to sense a nice looking saw tooth on the plotter byte ledBrightness = 0x1F; //Options: 0=Off to 255=50mA byte sampleAverage = 8; //Options: 1, 2, 4, 8, 16, 32 byte ledMode = 3; //Options: 1 = Red only, 2 = Red + IR, 3 = Red + IR + Green int sampleRate = 1000; //Options: 50, 100, 200, 400, 800, 1000, 1600, 3200 int pulseWidth = 411; //Options: 69, 118, 215, 411 int adcRange = 4096; //Options: 2048, 4096, 8192, 16384 particleSensor.setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange); //Configure sensor with these settings // initiate min, max values irValue = particleSensor.getIR(); max_irValue = irValue; min_irValue = max_irValue - 1; for (int ini ; ini < 100 ; ini++) { irValue = particleSensor.getIR(); if (max_irValue < irValue) {max_irValue = irValue;} if (min_irValue > irValue) {min_irValue = irValue;} } display.display(); } void loop() { irValue = particleSensor.getIR(); if( x > 127) { //refresh OLED screen display.clearDisplay(); lastx=0; x=1; scaler = (max_irValue - min_irValue) / 32; pre_min_irValue = min_irValue; // reset min, max value max_irValue = irValue; min_irValue = irValue - 1; } display.setTextColor(WHITE); ThisTime=millis(); // invert y values to fit OLED display if ( pre_min_irValue > irValue) { y = 31; } else { y = 31 - ((irValue - pre_min_irValue) / scaler); } // draw heartbeat lins display.drawLine(lastx, lasty, x, y, WHITE); lasty = y; lastx = x; // update min, max values if (max_irValue < irValue) {max_irValue = irValue;} if (min_irValue > irValue) {min_irValue = irValue;} ThisTime=millis(); if( y < UpperThreshold ) { if(BeatComplete) { BPM=ThisTime-LastTime; BPM=int(60/(float(BPM)/1000)); BPMTiming=false; BeatComplete=false; tone(2,1000,250); } if(BPMTiming==false) { LastTime=millis(); BPMTiming=true; } } if((y > LowerThreshold) && (BPMTiming)) BeatComplete=true; // display BMP display.fillRect(0,24,64,32,BLACK); display.setCursor(0,24); display.print(" BPM"); display.setCursor(0,24); display.print(BPM); // check finger is on the sensor if (irValue < 10000) { display.setCursor(1,1); display.print("NO Finger?"); } // display all data to OLED display.display(); x++; }
결과 동영상을 올려 봅니다.
FIN
이걸 구현해 보려고 3주간 주말 = 토, 일 이틀간 x 3 = 6일 동안 삽질의 연속이었습니다.
이 문제는 소스에서 scalar 를 구하기 위한 나누기를 해야 하는데, % 연산자를 사용했기 때문이었습니다.
* Modulo
- https://www.arduino.cc/reference/en/language/structure/arithmetic-operators/modulo/
% 연산자는 몫을 구하는 것이 아니라, 나머지를 구하는 연산자지요!!!
반대로 알고 있었던 것. 이걸 알아 차리는데 6일이나 걸렸습니다!!!
변수의 갯수도 줄여 보기도 하고, 각 값을 먼저 scale in 해서 결과를 내보기도 하고, 하여간 별짓 다 했습니다.
처음에 디버깅만 잘 했어도 이렇게 오래 걸리지는 않았을 듯 하네요.
디버깅 작업이 얼마나 중요한지 새삼 깨닳았습니다.
이렇게 하면 Serial Monitor 에도 이렇게 나오니 환장할 노릇이었습니다.
이 덕에 Hardware SPI 활용과 OLED refresh rate 등등을 공부하게 되었네요.
아이고야... 좀 쉬자.
SpO2 농도 값이나, 그래프를 좀더 안정적으로 보여줄 수 있도록 소스를 더 다듬고 싶었지만,
에너지를 너무 많이 써서 이번 작업은 여기까지 하겠습니다.
나중에 다시 생각나면, 소스코드를 다시 잡아 볼께요.
1. 파티클 센서
인터넷을 돌아다니다 particle sensor 라는 것을 알게 되었습니다.
이게 단순히 공기중의 입자를 확인하는것 뿐만아니라, 혈중 농도 및 심전도까지 확인할 수 있다고 합니다.
나중에 도플러 효과를 이용한 원격 심전도 확인 시스템을 만들 예정이라,
기초적인 지식을 습득하기 위해 일단 이런 류의 센서 사용법을 익혀 보기로 합니다.
2. 주문
물론 AliExpress 에서 주문했습니다.
* CFsunbird High Accuracy I2C MAX30105 Particle Optical Sensor Photodetectors Board Module 1.8V power supply
여타 센서류보다 단일 부품으로는 꽤나 비싼편 입니다.
이는 다양한 기능이 들어가 있으며, 기본 소자 자체가 좀 있어 보입니다.
SparkFun 에서 팔고 있는 제품인데, AliExpress 에서 구입하다 보니, 조금 변형된 중국 제품입니다.
단, 소스는 완벽하게 호환됩니다.
* SparkFun Particle Sensor Breakout - MAX30105
- https://learn.sparkfun.com/tutorials/max30105-particle-and-pulse-ox-sensor-hookup-guide
보통 정식 제품보다는 AliExpress 가 싼 편인데, 이 제품은 자비가 없군요.
AliExpress 에서 약 12 USD, SparkFun 제품은 13 USD 정도 합니다.
3. 도착
크게 무리없이 2주만에 도착.
납땜이 되지 않은 상태로 배달이 됩니다. 이제 AliExpress 에서 오는 것은 이게 당연한 거죠?
핵심이 되는 센서 부품을 클로즈 업 해봤습니다.
범상치 않아 보이는군요.
4. 연결
Arduino 와의 연결은 다음과 같습니다.
MAX30105 | Arduino Nano ------------------------- VCC | 3.3V GND | GND SDL | A5 SDA | A4 -------------------------
실제 회로 구성은 다음과 같습니다.
Fritzing 에서 그려 보는데, 아쉽게도 아직 Fritzing 에서는 MAX30105 부품이 리스트에 존재하지 않습니다.
아직 아무도 만들지 않았나 봅니다.
Datasheet 입니다 - MAX30105_3.pdf
5. sketch - Red, IR, Green reading
이후 나오는 소스는 모두 아래 site 를 참고하였습니다.
* SparkFun Particle Sensor Breakout - MAX30105
- https://learn.sparkfun.com/tutorials/max30105-particle-and-pulse-ox-sensor-hookup-guide
Library 를 인스톨 하고 가장 간단한 스케치를 로드하여 확인해 봅니다.
소스는 소자의 기본 동작인 빨간색, 적외선, 녹색을 감지하고 수치화 하는 스케치 입니다.
File > Examples > MAX3010x Pulse and Proximity Sensor > Example1_Basic_Readings
실제 소스를 여기에 옮겨 봅니다.
/* MAX30105 Breakout: Output all the raw Red/IR/Green readings By: Nathan Seidle @ SparkFun Electronics Date: October 2nd, 2016 https://github.com/sparkfun/MAX30105_Breakout Outputs all Red/IR/Green values. Hardware Connections (Breakoutboard to Arduino): -5V = 5V (3.3V is allowed) -GND = GND -SDA = A4 (or SDA) -SCL = A5 (or SCL) -INT = Not connected The MAX30105 Breakout can handle 5V or 3.3V I2C logic. We recommend powering the board with 5V but it will also run at 3.3V. This code is released under the [MIT License](http://opensource.org/licenses/MIT). */ #include "Wire.h" #include "MAX30105.h" MAX30105 particleSensor; #define debug Serial //Uncomment this line if you're using an Uno or ESP //#define debug SerialUSB //Uncomment this line if you're using a SAMD21 void setup() { debug.begin(9600); debug.println("MAX30105 Basic Readings Example"); // Initialize sensor if (particleSensor.begin() == false) { debug.println("MAX30105 was not found. Please check wiring/power. "); while (1); } particleSensor.setup(); //Configure sensor. Use 6.4mA for LED drive } void loop() { debug.print(" R["); debug.print(particleSensor.getRed()); debug.print("] IR["); debug.print(particleSensor.getIR()); debug.print("] G["); debug.print(particleSensor.getGreen()); debug.print("]"); debug.println(); }
우선 구동을 시작하면, 녹색과 빨간색 빛이 빠르게 점멸합니다.
저렇게 빛을 쏜 다음 반사되는 값을 읽는것이겠죠?
손가락을 센서 근처로 가져가면 값의 변동이 일어납니다.
센서의 불빛을 슬로우 모션으로 9초동안 찍어 봤습니다.
그랬더니 51초짜리 동영상이 되었네요.
얼마나 빨리 점멸을 하는지, 슬로모션으로 찍어도 실제로 보는것과 그리 차이나지 않습니다.
6. sketch - 물체 인식
IR 의 delta 값을 이용하여 물체가 일정 이상 안에 들어와 있는지 없는지를 판단합니다.
소스는 아래 path 에서 확인할 수 있습니다.
File > Examples > MAX3010x Pulse and Proximity Sensor > Example2_Presense_Sensing
7. sketch - 온도
SparkFun 사에서 제공되는 library 에서 "readTemperature()" 함수를 그대로 이용한 것 입니다.
그럼 위의 함수는 어떻게 되느냐 하면 아래와 같습니다.
// Die Temperature // Returns temp in C float MAX30105::readTemperature() { // Step 1: Config die temperature register to take 1 temperature sample writeRegister8(_i2caddr, MAX30105_DIETEMPCONFIG, 0x01); // Poll for bit to clear, reading is then complete // Timeout after 100ms unsigned long startTime = millis(); while (millis() - startTime < 100) { uint8_t response = readRegister8(_i2caddr, MAX30105_DIETEMPCONFIG); if ((response & 0x01) == 0) break; //We're done! delay(1); //Let's not over burden the I2C bus } //TODO How do we want to fail? With what type of error? //? if(millis() - startTime >= 100) return(-999.0); // Step 2: Read die temperature register (integer) int8_t tempInt = readRegister8(_i2caddr, MAX30105_DIETEMPINT); uint8_t tempFrac = readRegister8(_i2caddr, MAX30105_DIETEMPFRAC); // Step 3: Calculate temperature (datasheet pg. 23) return (float)tempInt + ((float)tempFrac * 0.0625);
센서 내부에 register 값을 읽어와서 표현하는 것이군요.
구동 소스는 아래 path 에서 확인할 수 있습니다.
File > Examples > MAX3010x Pulse and Proximity Sensor > Example3_Temperature_Sense
8. sketch - 심전도
손가락을 센서에 대고 있으면 심전도를 그려주는 소스 입니다.
File > Examples > MAX3010x Pulse and Proximity Sensor > Example4_HeartBeat_Plotter
9. sketch - 심박수
대체로 심전도와 비슷한데, 이걸 1분에 몇분 정도 뛰는지를 확인해 주는 소스 입니다.
보통 병원이나 건강검진시 확인해 보는 심박수 겠죠?
구동 소스는 아래 path 에서 확인할 수 있습니다.
File > Examples > MAX3010x Pulse and Proximity Sensor > Example5_HeartRate
FIN
MAX30105 센서는 주로, 심박수 모니터링에 사용되는게 가장 적절해 보입니다.
인터넷에서 어느 분이, 이와 비슷한 센서를 이용하여 심박수를 OLED 에 표시해주는 것까지 해놓은 글이 아래 입니다.
* Heart beat Sensor and “ECG” Display
- http://www.xtronical.com/basics/heart-beat-sensor-ecg-display/
저도 따라해 봤는데,
일단 센서에서 나오는 값 자체가 MAX30105 처럼 큰 값으로 나오지 않을 뿐더러,
값의 변화가 딱 OLED 크기만큼 잘 구현이 되어 있어서 잘 하신것 같은데, 도저히 따라해도 안되는군요.
겨우겨우 우겨 넣어서 OLED 에 보여주는 것 까지는 해 봤습니다.
이것 가지고는 만족스럽지 못하여, 못하는 코딩을 이용하여 한번 도전해 보겠습니다.
병원에서 보는 듯 한 모습으로 될 때까지 만들어서 공유해 볼께요.
'Hardware' 카테고리의 다른 글
Hardware | ATtiny85 개발 보드를 이용하여 Digispark 를 DIY 하기 (0) | 2018.12.14 |
---|---|
Hardware | 8x8 LED matrix 와 Colorduino 이용해 보기 (0) | 2018.12.03 |
Hardware | RTC DS3231 부품 사용기 - 2 (0) | 2018.11.20 |
Hardware | RTC DS3231 부품 사용기 - 1 (2) | 2018.11.11 |
Hardware | Transistor 구매 (0) | 2018.11.10 |
이 포스트는 앞전에 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, setA2Time 과 turnOnAlarm() 펑션이 있습니다.
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 를 보면 뭔가 더 많은데, 여기서 그만 하려구요.)
'Hardware' 카테고리의 다른 글
Hardware | 8x8 LED matrix 와 Colorduino 이용해 보기 (0) | 2018.12.03 |
---|---|
Hardware | MAX30105 파티클 센서 - 1 (0) | 2018.11.23 |
Hardware | RTC DS3231 부품 사용기 - 1 (2) | 2018.11.11 |
Hardware | Transistor 구매 (0) | 2018.11.10 |
Hardware | 스위치 부품 구매하기 (0) | 2018.11.09 |
이 포스트는 DS3231 에 대한 이야기 이며, 후속편에 이어집니다.
* Hardware | RTC DS3231 부품 사용기 - 2
- http://chocoball.tistory.com/entry/Hardware-RTC-usning-DS3231-2
1. RTC
보통 internet 이 달린 기기라면 NTP Server 와 연동하여 시간을 맞추고,
특정 시간에 정확하게 일을 시킬 수가 있습니다.
Internet 에 연결되지 않은 기기의 경우는 다음과 같은 과정이 필요하겠죠.
A) 시간을 설정한다.
B) 시간을 기억한다.
C) 특정 시간에 일을 시킨다.
Internet 이 연결되지 않은 기기와 비교해 보면 B) 항목이 필요합니다.
이 "시간을 기억" 하고, 언제든지 현재 시간 정보를 가져올 수 있는 부품이 DS3231 입니다.
그래서 이번에는 DS3231 을 구입하고 사용해 보겠습니다.
자세한 내용은 아래 Arduino 사이트를 참조해 보세요.
* RTC Library
- https://www.arduino.cc/en/Reference/RTC
2. 구입
말할것도 없이 AliExpress 입니다.
배송까지 40일 걸렸습니다. 이정도 되면 배송에 대해서는 해탈해야 합니다.
* 1PCS DS3231 AT24C32 IIC Precision RTC Real Time Clock Memory Module For Arduino new original
건전지 미포함 1.06 USD 무료배송이면 고민거리는 아닙니다.
(배송기간 빼고)
인터넷을 뒤지니, 위의 부품으로 거의 통일되어 있는것 같았습니다.
3. 도착
요로코롬 도착했습니다.
부품 확대 사진입니다.
메인 chip 에 DS321 이라고 적혀 있네요.
발진기인 오실레이터도 보이고, 밑에는 AT24C32 EEPROM (32Kb) 도 있습니다.
참고로 DS3231 칩 안에는 추가로 온도센서도 존재합니다.
- Data sheet : DS3232.pdf
특이하게 "왜 온도센서?" 냐 하면,
전자 발진기 - 오실레이터 - 는 온도에 따라 그 값이 변합니다.
그래서 온도에 따른 변화를 보정하기 위해 온도센서가 자리잡고 있는 것이지요.
뒷면에는 CR2032 버튼 전지를 끼울 수 있는 플라스틱이 존재합니다.
이는 전원이 차단되더라도 "시간을 기억" 하기고 있기 위한 것이지요.
이 건전지 한개로 몇년은 쓴다고 하네요.
4. Layout
배선은 일반 IIC 배선과 동일합니다.
SLA 은 A4에, SCL은 A5 이죠.
DS3231 | Arduino Nano ------------------------- VCC | 3.3V GND | GND SDC | A5 SDA | A4 ------------------------- SSD1306 | Arduino Nano ------------------------- VCC | 3.3V GND | GND SDC | A5 SDA | A4 -------------------------
실재 배선 모양입니다.
I2C의 특성상, 다른 센서 / 부품들 중, I2C 방식이면 arduino 의 동일한 pin 에 꼽아도 따로 인식 됩니다.
이는 각 device 가 가지는 address 가 다르기 때문이지요.
이는 I2C Scanner 를 이용해서 살펴보면, 각각 따로 인식하는 것을 알 수 있습니다.
아래에서 0x3c 는 OLED 이고, 0x68 이 DS3231 입니다.
추가로 나오는 0x57 은 AT24C32 EEPROM 입니다.
여기서 이상한 점은 0x5f 라는 address 입니다.
무얼까... 답을 찾지는 못했지만, 찾는 와중에 한가지 새로운 사실을 알게 됩니다.
5. EEPROM
EEPROM 으로는 ATmega 사의 AT24C32 이 쓰입니다.
이것의 실제 chip 번호는 ATML332 라고 적혀 있습니다.
- https://www.kynix.com/Detail/447536/ATMLH745.html
그렇습니다. EEPROM 이 original 이 아니고 fake 제품인 것이죠.
그러나 제품 구동은 정상적으로 돌아갑니다.
100% original chip 과 동일하지 않기 때문에 보다 복잡한 작업을 시키면 정상적으로 동작하지 않을 지도 모릅니다.
여튼, 앞으로 싼 부품은 좀 걸러야 할지도 모르겠네요.
6. Library 등록
인터넷에 돌아다니는 source 를 등록해서 사용할 수 있지만,
IDE 에서 지원해주는 Library 등록 기능을 이용하여 Example source 를 등록해 봅니다.
우선 IDE 메뉴에서 "Sketch > Add File... > Manage Libraries..." 를 선택합니다.
이건 이제 매번 써먹는 방법이지요?
Libarry Manager 의 검색창에서 "ds3231" 을 쳐서 검색합니다.
그러면 여러가지 source 들이 나오는데, 왠만하면 제일 위에 나오는 것을 선택하면 됩니다.
아래 그림처럼 adafruit 에서 만든 library 이니, 쓸만 할껍니다.
이렇게 하면 IDE 메뉴의 "File > Examples > DS3231" 항목이 생기고 sample source 를 이용할 수 있습니다.
6. sketch - 시간 설정
인터넷에 돌아다니는 source 를 등록해서 사용할 수 있지만,
위에서처럼 Arduino IDE 의 Library Manager 를 통해서 얻은 소스를 활용해 봅니다.
일단 시간을 입력합니다.
소스에 보이듯이 Serial Monitor 에 "YYMMDDwHHMMSS" 를 넣고, 마지막에 "x" 를 붙이면 설정됩니다.
------------------------------------------------
YYMMDDwHHMMSS, with an 'x' at the end
------------------------------------------------
/* Sets the time and prints back time stamps for 5 seconds Based on DS3231_set.pde by Eric Ayars 4/11 Added printing back of time stamps and increased baud rate (to better synchronize computer and RTC) Andy Wickert 5/15/2011 */ #include "DS3231.h" #include "Wire.h" DS3231 Clock; byte Year; byte Month; byte Date; byte DoW; byte Hour; byte Minute; byte Second; bool Century=false; bool h12; bool PM; void GetDateStuff(byte& Year, byte& Month, byte& Day, byte& DoW, byte& Hour, byte& Minute, byte& Second) { // Call this if you notice something coming in on // the serial port. The stuff coming in should be in // the order YYMMDDwHHMMSS, with an 'x' at the end. boolean GotString = false; char InChar; byte Temp1, Temp2; char InString[20]; byte j=0; while (!GotString) { if (Serial.available()) { InChar = Serial.read(); InString[j] = InChar; j += 1; if (InChar == 'x') { GotString = true; } } } Serial.println(InString); // Read Year first Temp1 = (byte)InString[0] -48; Temp2 = (byte)InString[1] -48; Year = Temp1*10 + Temp2; // now month Temp1 = (byte)InString[2] -48; Temp2 = (byte)InString[3] -48; Month = Temp1*10 + Temp2; // now date Temp1 = (byte)InString[4] -48; Temp2 = (byte)InString[5] -48; Day = Temp1*10 + Temp2; // now Day of Week DoW = (byte)InString[6] - 48; // now Hour Temp1 = (byte)InString[7] -48; Temp2 = (byte)InString[8] -48; Hour = Temp1*10 + Temp2; // now Minute Temp1 = (byte)InString[9] -48; Temp2 = (byte)InString[10] -48; Minute = Temp1*10 + Temp2; // now Second Temp1 = (byte)InString[11] -48; Temp2 = (byte)InString[12] -48; Second = Temp1*10 + Temp2; } void setup() { // Start the serial port Serial.begin(57600); // Start the I2C interface Wire.begin(); } void loop() { // If something is coming in on the serial line, it's // a time correction so set the clock accordingly. if (Serial.available()) { GetDateStuff(Year, Month, Date, DoW, Hour, Minute, Second); Clock.setClockMode(false); // set to 24h //setClockMode(true); // set to 12h Clock.setYear(Year); Clock.setMonth(Month); Clock.setDate(Date); Clock.setDoW(DoW); Clock.setHour(Hour); Clock.setMinute(Minute); Clock.setSecond(Second); // Give time at next five seconds for (int i=0; i<5; i++){ delay(1000); Serial.print(Clock.getYear(), DEC); Serial.print("-"); Serial.print(Clock.getMonth(Century), DEC); Serial.print("-"); Serial.print(Clock.getDate(), DEC); Serial.print(" "); Serial.print(Clock.getHour(h12, PM), DEC); //24-hr Serial.print(":"); Serial.print(Clock.getMinute(), DEC); Serial.print(":"); Serial.println(Clock.getSecond(), DEC); } } delay(1000); }
시각을 입력하니 잘 등록되고 읽어집니다.
7. sketch - 시간 설정 + 시간 가져오기 + 온도
기본 sample 을 사용해도 되지만, 찾아다니면서 하나로 된 소스는 아래인것 같습니다.
Serial Monitor 의 입력창에 "T1124154091014" 등으로 입력하면 시각이 입력되면서,
그냥 와두면, "시간 + 온도" 를 표시해 준다.
* Tutorial – Using DS1307 and DS3231 Real-time Clock Modules with Arduino
#include "Wire.h" #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; void setup() { Wire.begin(); Serial.begin(9600); } void loop() { watchConsole(); get3231Date(); Serial.print(weekDay); Serial.print(", "); Serial.print(date, DEC); Serial.print("/"); Serial.print(month, DEC); Serial.print("/"); Serial.print(year, DEC); Serial.print(" - "); Serial.print(hours, DEC); Serial.print(":"); Serial.print(minutes, DEC); Serial.print(":"); Serial.print(seconds, DEC); Serial.print(" - Temp: "); Serial.println(get3231Temp()); 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); 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; }
Serial Monitor 의 결과 입니다.
8. sketch - 시간 설정 + 시간 가져오기 + 온도 + OLED
위의 소스를 조금 바꾸어 OLED 에 표시해주는 소스로 살짝 바꾸었습니다.
일주일 지난 뒤, 측정하니 여전히 잘 동작하고 있네요.
살짝 바꾼 소스 올려 봅니다.
#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; 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); display.print("DATE : "); display.print(weekDay); display.print(", "); display.print(date, DEC); display.print("/"); display.print(month, DEC); display.print("/"); display.println(year, DEC); display.print("TIME : "); display.print(hours, DEC); display.print(":"); display.print(minutes, DEC); display.print(":"); display.println(seconds, DEC); 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); 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; }
동영상도 올려 봅니다.
FIN
DS3231 에 대해서는 이야기 할 내용이 더 있어서 2편에서 더 다루어 보겠습니다.
'Hardware' 카테고리의 다른 글
Hardware | MAX30105 파티클 센서 - 1 (0) | 2018.11.23 |
---|---|
Hardware | RTC DS3231 부품 사용기 - 2 (0) | 2018.11.20 |
Hardware | Transistor 구매 (0) | 2018.11.10 |
Hardware | 스위치 부품 구매하기 (0) | 2018.11.09 |
Hardware | ipTIME N704BCM 무선 공유기 (2) | 2018.11.06 |
1. 이제는 transistor 까지 왔다
이 취미를 하다 보면, 당연히 여기까지 오게 되어 있습니다.
어쩌면 늦은 감이 없지 않아 있죠.
그렇습니다. 트랜지스터 입니다. 짜잔~!
트랜지스터는 오늘날의 IC 칩이 나오게 된 기초 소자였으며,
현재의 전자혁명을 가져오게 한 근본되는 소자 입니다.
트랜지스터는 두가지 역할이 있습니다.
- 증폭
- on/off 스위치
자세한 동작 원리는 internet 에 흘러 넘치므로 특별히 여기서는 다루지 않을께요.
2. 구매
오늘도 예외없이 AliExpress 의 바다를 헤엄칩니다.
가장 널리 쓰이는 트랜지스터 소자 중, 그 두가지가 BC547, BC557 이라고 하네요.
* (50Pcs/lot)BC547+BC557 Each 25Pcs BC547B BC557B NPN PNP Transistor TO-92 Power Triode Transistor kit Bag
여러 종류가 한묶음으로 파는 판매자도 있어, 아래 제품도 함께 구매합니다.
* 170PCS Transistor Assorted Kit S9012 S9013 S9014 9015 9018 A1015 C1815 A42 A92 2N5401 2N5551 A733 C945 S8050 S8550 2N3906 2N3904
3. 기호 읽기
트랜지스터에 씌여 있는 숫자 및 기호 읽는 방법은 아래와 같습니다.
* Reading_Transistor_Markings.pdf
즉, 소재의 종류, 사용처, 그리고 연번 순서네요.
예로, BC547 은 다음과 같은 성질을 갖는 것이죠.
- B : Sillicon
- C : Transistor - audio frequency, low power
- 547 : Serial number
아주 자세한 specification : https://components101.com/bc547-transistor-pinout-datasheet
좀더 전기적인 특성은 위의 spec. sheet 를 봐야겠지만, 씌여진 내용으로만 봐도 실리콘으로 만들어진
라디오에 적합한 트랜지스터라는 것을 알 수 있습니다.
위의 PDF 에도 설명이 잘 되어 있지만,
좀더 보기 쉽게 만들어진 웹사이트가 있어서 그 내용을 출처와 함께 여기에 남깁니다.
* Transistor & Diode Numbering Codes
4. 도착 및 확인
전기적인 특성 모두를 트랜지스터에 표기된 숫자 및 기호만으로는 알 수가 없어,
transistor tester 를 이용하여 보여주는 값들을 정리해 봅니다.
분명 트랜지스터를 가지고 놀게 되면 이 data 가 요긴하게 쓰일꺼라 생각합니다.
BC547 (BJT-NPN)
| hFE | Ic | Ube | ICEO | ICEs | ------------------------------------- | 351 | 3.3mA | 651mV | 4uA | 2uA |
BC557 (BJT-NPN)
| hFE | Ic | Ube | ICEO | ICEs | ------------------------------------- | 393 | 3.6mA | 659mV | 1uA | - |
S9012 (BJT-PNP)
| hFE | Ic | Ube | ICEO | ICEs | ------------------------------------- | 272 | 2.5mA | 654mV | - | - |
S9013 (BJT-NPN)
| hFE | Ic | Ube | ICEO | ICEs | ------------------------------------- | 144 | 1.4mA | 617mV | 2uA | 5uA |
S9014 (BJT-NPN)
| hFE | Ic | Ube | ICEO | ICEs | ------------------------------------- | 336 | 3.1mA | 651mV | 4uA | 2uA |
S9015 (BJT-PNP)
| hFE | Ic | Ube | ICEO | ICEs | ------------------------------------- | 362 | 3.4mA | 646mV | - | - |
S9018 (BJT-NPN)
| hFE | Ic | Ube | ICEO | ICEs | -------------------------------------- | 106 | 0.99mA | 717mV | 2uA | 5uA |
A1015 (BJT-PNP)
| hFE | Ic | Ube | ICEO | ICEs | -------------------------------------- | 305 | 2.8mA | 642mV | - | - |
C1815 (BJT-NPN)
| hFE | Ic | Ube | ICEO | ICEs | -------------------------------------- | 373 | 3.5mA | 651mV | 4uA | 2uA |
A42(KSP42) (BJT-NPN)
| hFE | Ic | Ube | ICEO | ICEs | -------------------------------------- | 157 | 1.5mA | 607mV | 1uA | 4uA |
A92(KSP92) (BJT-PNP)
| hFE | Ic | Ube | ICEO | ICEs | ------------------------------------- | 140 | 6.2mA | 648mV | - | - |
2N5401 (BJT-PNP)
| hFE | Ic | Ube | ICEO | ICEs | ------------------------------------- | 194 | 1.8mA | 633mV | - | - |
2N5551 (BJT-NPN)
| hFE | Ic | Ube | ICEO | ICEs | -------------------------------------- | 127 | 1.2mA | 620mV | 5uA | 2uA |
A733 (BJT-PNP)
| hFE | Ic | Ube | ICEO | ICEs | ------------------------------------- | 252 | 2.3mA | 655mV | - | - |
C945 (BJT-NPN)
| hFE | Ic | Ube | ICEO | ICEs | -------------------------------------- | 351 | 3.3mA | 652mV | 4uA | 2uA |
S8050 (BJT-NPN)
| hFE | Ic | Ube | ICEO | ICEs | -------------------------------------- | 226 | 2.1mA | 594mV | 1uA | 2uA |
S8550 (BJT-PNP)
| hFE | Ic | Ube | ICEO | ICEs | ------------------------------------- | 301 | 2.8mA | 643mV | - | - |
2N3906 (BJT-PNP)
| hFE | Ic | Ube | ICEO | ICEs | ------------------------------------- | 185 | 1.7mA | 643mV | - | - |
2N3904 (BJT-NPN)
| hFE | Ic | Ube | ICEO | ICEs | -------------------------------------- | 370 | 3.4mA | 657mV | 7uA | 4uA |
FIN
트랜지스터 한아름이 있으니, 마음이 풍족하군요!
어떤 프로젝트를 하든 바로 확인해 볼 수 있는 부품이 생겼습니다.
'Hardware' 카테고리의 다른 글
Hardware | RTC DS3231 부품 사용기 - 2 (0) | 2018.11.20 |
---|---|
Hardware | RTC DS3231 부품 사용기 - 1 (2) | 2018.11.11 |
Hardware | 스위치 부품 구매하기 (0) | 2018.11.09 |
Hardware | ipTIME N704BCM 무선 공유기 (2) | 2018.11.06 |
Hardware | planefinder unboxing (2) | 2018.10.30 |
1. 스위치, 스위치, 스위치...
전자 공작(?) 을 하다 보면, 스위치 부품이 필요할 때가 옵니다.
저의 경우는 ESP8266 의 firmware 올릴 때가 그 때였네요.
* Hardware | ESP-01 or ESP8266 사용기 - 1
- http://chocoball.tistory.com/entry/Hardware-ESP01-or-ESP8266-using-1
2. 구매
우리의 친구 AliExpress 에서 검색해 봅니다.
빵판에서 사용할 수 있는 버튼을 찾아보니 아래 제품이 나오네요.
이 스위치는 눌렀다가 힘을 빼면 접점이 없어지는 스위치 입니다. 보통 PC 의 reset 버튼 같은거죠.
영어 명칭은 "tactile button" 이라고 합니다.
* Smart Electronics Tactile Push Button Switch Momentary 12*12*7.3MM Micro Switch Button + 5 Colors Tact Cap
위는 OMRON 사의 B3F-4055 의 카피품 입니다.
참고로 ORMON 사의 B3F 형식의 스위치 설명서 입니다.
내구성은 차이가 나겠으나, 기본 동작용으로 사용하기엔 이만한게 없을것 같습니다.
또다른 제품은 10가지 각종 스위치가 들어가 있는 제품입니다.
SMD 실장용도 들어가 있네요.
* Smart Electronics 10 Kinds of Tactile Switches Push Button SMD Tact Switch Switch 100pcs
PC의 전원처럼 한번 꾹~ 누르면 회로적으로 short 되었다가,
다시 누르면 open 되는 스위치부터, 자동차 remote key 처럼 손끝으로 눌림이 느껴지는 SMD 스위치까지 다양하게 들어 있습니다.
이게 1.90 USD 라뉘~~~. 여기에 무료 배송은 덤.
3. 도착
배송기간이 거진 한달이 되는거 빼곤 잘 도착했습니다.
4. 각 스위치의 모양
우선 tactile 버튼 입니다.
한놈은 다리가 구부러져서 왔네요.
대충 벌려서 빵판에 눌러주면 적당한 크기로 알아서 펴 집니다.
누를 때 스위치같이 느껴지려면 모자를 씌워주면 됩니다.
윗면의 돌기가 모자의 갈고리에 이쁘게 맞춰서 들어갈 수 있도록 설계되어 있습니다.
물론 쉽게 제거도 가능합니다.
참 아기자기 한 스위치 입니다.
요놈은 SMD 방식으로 기판 위에 실장되는 tactile button 입니다.
대충 눌러도 되면서 SMD 방식으로는 이게 많이 쓰이는것 같습니다.
아두이노의 reset 스위치에도 이게 쓰이는것 같아요.
위의 tactile button 의 다리가 긴 버전입니다.
through hole 에 끼워서 납땜할 수 있도록 다리가 길게 되어 있습니다.
물론 사진에서처럼 빵판에 끼워서도 사용 가능합니다.
다음은 desktop 의 case 에 가장 많이 쓰이는 스위치 입니다.
한번 누르면 눌려진 상태로 고정되는 (close) 스위치와,
reset 버튼처럼, 누르는 힘이 빠지면 바로 올라오는 (tactile) 스위치 입니다.
눌렀을 때, 어디가 새로 closed 되는지 확인해 보니, 양쪽을 마주보는 6개의 다리 중에서
대각선 다리끼리 스위치를 눌렀을 때 쇼트 됩니다.
왜 다리가 6개인지는 아직 잘 모르겠습니다.
위의 스위치는 슬라이드로 on/off 기능하는 스위치 입니다. 많이 쓰이는거죠
아래는 tactile button 인데, 기판을 관통해서 납땜하는 버전입니다.
아래부터는 SMD 형식입니다..
다리가 4개짜리도 있고, 2개짜리도 있고.
헤드가 많이 튀어 나온 것도 있고, 조금만 튀어 나온것도 있고.
납작한 것도 있고.
위의 버튼은 arduino nano 의 중국 copy 버전에서 자주 보는 버튼이네요. :-)
5. 스위치 가족
한데 모아서 찍어봤습니다.
이제 swich 나 button 은 추가로 구매할 일은 거의 없겠죠?
'Hardware' 카테고리의 다른 글
Hardware | RTC DS3231 부품 사용기 - 1 (2) | 2018.11.11 |
---|---|
Hardware | Transistor 구매 (0) | 2018.11.10 |
Hardware | ipTIME N704BCM 무선 공유기 (2) | 2018.11.06 |
Hardware | planefinder unboxing (2) | 2018.10.30 |
Hardware | ESP-01 or ESP8266 사용기 - 1 (4) | 2018.10.04 |
1. 시작
아두이노에 연결해서 사용할 수 있는 저가의 Wi-Fi 모듈로는,
유명한 Espressif Systems 사의 ESP8266 와, Ai-Thinker 사의 ESP-01 모듈이 있습니다.
다른 여타 sensor 나 module 처럼 금방 사용할 수 있겠지 하고 덤볐다가, 지옥이 열렸습니다.
* ESP8266
- https://en.wikipedia.org/wiki/ESP8266
저가이면서 Wi-Fi 구성이 된다니, 신기할 따름입니다.
바로 구매하여 확인해 봅니다.
2. 구매
AliExpress 에서 쉽게 검색이 됩니다.
외형이 살짝 다른 두 종류가 있어서 두가지 모두 구입해 봅니다.
한개는 8Mb flash memory 라고 하는군요.
* ESP-01, ESP8266,WIFI module 8Mb flash memory
- https://www.aliexpress.com/item/WIFI-module-ESP-01-ESP8266-8Mb-flash-memory/32733744011.html
* Upgraded version ESP-01 ESP8266 serial WIFI wireless module wireless transceiver ESP01 ESP8266-01
3. 외형
모양은 이렇게 생겼습니다.
제조사는 다르지만 기본 chip 및 구성은 거의 동일합니다.
위의 그림에서 8Mbit Flash 라는 제품이 밑에 보이는 것인데,
memory chip 두께가 살짝 더 두꺼워 보입니다.
평범한 뒷모습.
4. Pin 배열
Pin out 이 2열로 되어 있어서, 빵판에서 그냥 꼽으면 short 가 발생합니다.
점퍼선으로 연결해도 되지만 깔끔하지 못할 뿐더러 연결시 자꾸 헷갈리기도 합니다.
Wi-Fi 모듈 보드 한쪽이 안테나를 형성하고 있어서,
이렇게 한쪽으로 모두 pin 을 모아야 하는 것은 이해가 갑니다만 빵판에서는 최악입니다.
꽤나 불편합니다.
AliExpress 에서 우연하게 breadboard 에서 편하게 사용할 수 있도록 해주는 adapter 를 발견하였습니다.
* 2PCS For ESP-01 Esp8266 ESP-01S Model Of The ESP8266 Serial Breadboard Adapter To WiFi Transceiver Module Breakout UART Module
아래는 실재 사용한 사진입니다.
수직을 수평으로 피면서 양쪽으로 pin 들을 분리해주는
이 adapter 를 사용하면, 이쁘게 양쪽으로 pin 들을 구분해 줍니다.
여러분들도 꼭 구입해 보아요.
5. 먼저 알고 있어야 할 것들 - BAUD RATE
ESP8266 은 쉽게 접근할 수 있는 모듈이 아닙니다.
값싸고 성능이 괜찮은 대신, 문제 없이 구동시키려면 몇 가지 조건이 충족되어야 합니다.
이런 배경지식 없이 덥볐다가 시행착오에 꽤 많은 시간을 쏟아 부어야 했습니다.
거의 모든 ESP8266 모듈들은 공장 출하시 UART serial IO 속도가 115200 으로 정해져 있습니다.
Arduino Mega 와 같이 HW Serial 이 두개면 문제가 없습니다.
단, Arduino Uno/Nano 의 경우, 하나밖에 없는 HW Serial 을 USB 연결용으로 사용해 버리므로 문제가 됩니다.
결국, Arduino Uno/Nano 는 ESP8266 와 SoftwareSerial 로 연결되어야 하나,
SoftwareSerial 은 115200 처럼 높은 baud rate 를 지원하지 않습니다.
그래서 연결하려는 arduino 가 Uno/Nano 라면, BAUD RATE 를 변경해 줄 필요가 있습니다.
다음은 AT 명령어를 이용하여 통신 속도를 변경하는 방법 입니다.
AT+UART_DEF= baudrate , databits , stopbits , parity , flow control
보통 9600 으로 설정시 다음과 같은 명령어를 사용합니다.
AT+UART_DEF=9600,8,1,0,0
여기서 주의할 점은, ESP8266 에 구워진 AT 명령어 firmware 버전에 따라 사용하면 안되는 명령어들이 있습니다.
"AT+CIOBAUD=9600" 나 "AT+IPR=9600" 는 일시적으로만 동작되거나 ESP8266 을 벽돌로 만들어 버릴 수도 있습니다.
그러므로, 항상 최신버전의 AT firmware 를 먼저 굽고 사용해야 합니다.
firmware upgrade 에 대해서는 아래에서 자세하게 다룹니다.
6. 먼저 알고 있어야 할 것들 - 충분한 전류
ESP8266 모듈은 전력을 많이 소비합니다. 250mA 정도는 사용한다고 하네요.
ESP8266 구동에 필요한 3.3V 를 지원하기는 하지만,
200mA 이상 나오지 않는 Nano 의 3.3V 포트에 연결하면 정상적으로 동작하지 않습니다.
전류가 부족해서 나타나는 증상은, LED indicator 가 정상적으로 점멸하지 않다거나,
(아래 사진은 추가 전원을 이용하여 정상적으로 동작하는 모습)
AT 명령어에 대한 response 가 중구난방입니다.
전력을 충분히 공급하는 회로로는 3가지가 있습니다.
하나. arduino + level shifter
Uno 5V 포트는 3.3V 포트에 비해 더 많은 전류를 지원해 주지만,
거의 300mA 에 육박하는 전류를 커버하기 위해서는 외부 전원장치가 필요하므로, 이 방법은 시도해 보지 않았습니다.
둘. 외부 전력 공급장치
아래 보이는 것처럼 외부 전력 공급장치를 사용하는 것입니다.
예전 빵판 구입시 딸려 온 것을 사용해 봤습니다.
* Hardware | MB102 Breadboard Power Supply Module 를 사용해 보자
- http://chocoball.tistory.com/entry/Hardware-MB102-Breadboard-Power-Supply-Module
셋. FTDI USB 모듈이나 그 호환 모듈
FTDI 나 CP2102 를 사용하면, 중간에 arduino 와 연결할 필요 없이 PC 와 직접 연결이 가능하며,
모듈 자체적으로 3.3V 및 충분한 전류가 공급됩니다.
저 개인적으로는 FTDI 모듈보다 CP2102 가 더 안정적인 동작을 보이는것 같습니다.
참고로 FTDI 일 경우, 장치 관리자에서 단순히 "USB Serial Port" 로 보여서 port 번호를 알 수 없습니다.
(Arduino IDE 를 띄우면 port 정보가 나오기는 함)
이는 Driver install 시에 보면, Driver 2개가 서로 연동 하면서 변경되는 port 정보를 알 필요 없이 만들어주기 위한 방법으로 보입니다만,
저에겐 오히려 귀찮은 방식입니다.
반면 CP2102 일 경우, 포트번호가 표시되므로, putty 를 이용하여 serial 접속시 포트번호를 지정할 수 있습니다.
앞으로 ESP8266 에 관련된 확인은 CP2102 를 가지고 진행하겠습니다.
7. 먼저 알고 있어야 할 것들 - 최신 firmware
AliExpress 를 통해서 구매한 firmware 들은 2015년 이전 버전을 그대로 적용해서 출하하고 있습니다.
Firmware 버전이 너무 낮으면, 대응되는 명령어도 적을 뿐더러 뭔가 많이 불안한 반응을 보입니다.
가능하면 최신 버전으로 flash 해줘야 마음이 편합니다.
Ai-Thinker 사에서 최신 firmware 라고 올라와 있는 것을 간단하게 flash 해서 update 한 결과 입니다.
여기까지 오는데 8개월 걸렸네요.
그렇습니다.
결국 이 ESP8266 을 잘 쓰려면, firmware flash 를 잘 해놓는게 가장 기본이 됩니다.
그러기 위해서는 충분한 전류를 공급하는 전원도 구비해야 하는 것 이구요.
8. 먼저 알고 있어야 할 것들 - flash 파일
Flash 파일은 몇가지 종류와 버전이 존재합니다.
Espressif Systems 사에서 공개한 일반적인 버전의 flash file 과, Ai-Thinker 사가 공개한 flash file 이 있습니다.
오늘 flashing 하려는 것은 Ai-Thinker 사의 Wi-Fi 모듈이므로, 해당 모듈용 최신 파일을 준비합니다.
* ESP8266 最新SDK发布
- http://wiki.ai-thinker.com/esp8266/sdk
- ai-thinker_esp8266_dout_aicloud_v0.0.0.6_20170517.7z
압축을 풀면 몇가지 버전이 나오는데, 저는 8Mbit 인것 같아서, 작은 사이즈의 8Mbit 을 사용합니다.
또한 flashing 할 때, flash file 별로 메모리상의 주소를 지정해 줘야 합니다.
다행히 Ai-Thinker 사는 한 뭉탱이로 flash file 을 만들어 놔서, 주소가 메모리 첫번째 부터 쓰게끔 "0x00000" 을 지정하면 됩니다.
다른 버전과 좀더 복잡한 내용은 다음 post 에서 다루도록 하겠습니다.
(내용이 너무 넘처남...)
9. 먼저 알고 있어야 할 것들 - flash tool
Firmware upgrade 를 위한 flash tool 로는 몇가지가 있지만,
저는 "Espressif Systems" 사에서 공개하고 있는 "FLASH_DOWNLOAD_TOOLS" 가 사용하기 편했습니다.
* FLASH_DOWNLOAD_TOOLS V3.6.4
- flash_download_tools_v3.6.4_0.rar
* FLASH_DOWNLOAD_TOOLS V2.4
- FLASH_DOWNLOAD_TOOLS_v2.4_150924.7z
V3.6.4 에서는 baud rate 가 기본 115200 이상만 지원합니다.
만일 arduino 와 연결을 위해 9600 으로 낮추게 설정 했을 경우에는 firmware 를 upgrage 하기 위해 AT+UART_DEF 를 사용해야 하나,
혹시 그 firmware version 이 낮아서 이 command 를 못 알아먹을 경우에는 방법이 없습니다.
그럴 때에는 version 이 낮아서 조금 찜찜하기는 하나, 9600 을 지원하는 V2.4 를 사용하면 됩니다.
이제 "어떻게" 잘 firmware flash 를 하는지를 알아봐야겠습니다.
10. Firmware flashing 회로
Flashing 을 위한 회로는 몇가지가 있지만, 저는 아래 글을 참고하였습니다.
* Update the Firmware in Your ESP8266 Wi-Fi Module
- https://www.allaboutcircuits.com/projects/update-the-firmware-in-your-esp8266-wi-fi-module/
CH_PD 핀에 전원 인가 시 필히 저항을 달았으며,
RST 에 reset switch 와 GPIO0 에 flash 용 swtich 를 달았습니다.
똑딱이 스위치를 이 회로를 구성하기 위해서 구입했더랬습니다!!!
* Hardware | 스위치 부품 구매하기
- http://chocoball.tistory.com/entry/Hardware-buying-switch-components
스위치의 사용법은 다음과 같습니다.
1. RST 의 스위치를 누른다.
2. FLASH 의 스위치를 누른다.
3. RST 의 스위치에서 손을 뗀다.
4. FLASH 의 스위치에서 손을 뗀다.
5. Flash program 에서 "시작" 을 누른다.
안정적으로 전원이 공급되므로 LED 가 정상으로 점등, 점멸 합니다.
11. Firmware flashing
굳이 V2.4 을 이용하여 flash 하였습니다.
위에서 열거한 방법을 반복해 보자면, 아래처럼 진행하면 됩니다.
1. RST 의 스위치를 누른다. 2. FLASH 의 스위치를 누른다. 3. RST 의 스위치에서 손을 뗀다. 4. FLASH 의 스위치에서 손을 뗀다. 5. Flash program 에서 "시작" 을 누른다.
여기까지 오는데 8개월이 걸렸습니다.
눈물좀 훔치겠습니다... ㅠ.ㅠ
정상적으로 진행되는 과정의 스샷 입니다. 5% 진행되었을 때 캡춰했네요.
아래는 flash start 누른 후의 화면들을 캡춰 했습니다.
여러가지 확인하는 과정들이 있네요.
동영상으로 떠 봤습니다.
지금 다시 봐도 감격스럽네요.
12. ESP8266 에 console 로 접속하기
PC 에서 console 접속하려면 terminal 어플이 필요합니다.
여기서는 open source 이면서 사용하기 편한 putty 를 이용했습니다.
* Download PuTTY: latest release (0.70)
- https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html
이제 문제없이 접속이 되니, putty 를 통해서 serial port 로 연결합니다.
AT 를 치고, Ctrl + M, J 하면 타이핑한 내용이 ESP8266 에 전송되고 그 결과를 보여줍니다.
접속 mode 라던지, 현재 상태, 그리고 집에서 쓰는 Wi-Fi AP 에서 할당받은 IP 를 확인해 봤습니다.
(따로 AT+CWJAP 이라는 command 를 통해서 접속하는 과정이 필요)
위에서 마지막에 나오는 "192.168.123.135" 이 ESP8266 이 할당받은 IP 입니다.
ESP8266 은 내부에 web server 를 탑재하고 있어서 browser 를 통해서도 접속이 가능합니다.
이 외에 여러가지 AT command 들이 있습니다만,
글이 너무너무 길어지기에 오늘은 간단한 결과 들만 올려 봅니다.
(누차 이야기 하지만, 여기까지 오는데 8....)
13. ESP8266 에 web 으로 접속하기
브라우저에서 접속해본 스샷입니다.
몇가지 설정을 web 을 통해서도 수정할 수 있게 되어 있네요.
아래는 web 에서 제공하는 "REBOOT" 버튼을 클릭하고 얻은 결과 입니다.
정상적으로 잘 동작 합니다.
FIN
오랜 시행착오의 시간이 지나갔습니다. (사실 주말 가끔밖에 시간이 안나서...)
ESP8266 은 기능이 다양하고 강력한 대신, 길들여서(?) 사용하기가 여간 까다롭지 않습니다.
배경 지식도 많이 필요하구요.
다른 블로그 글들을 보면, 다들 쉽게 하던데 왜 나는 이렇게 어렵게 하는지 모르겠습니다.
이왕 여기까지 온거, 완벽하지는 않지만 납득이 가는 선까지 정리해 보고자 합니다.
다음 포스트에서는 나머지 이야기들을 정리해 보겠습니다.
'Hardware' 카테고리의 다른 글
Hardware | ipTIME N704BCM 무선 공유기 (2) | 2018.11.06 |
---|---|
Hardware | planefinder unboxing (2) | 2018.10.30 |
Hardware | Arduino 를 DIY 해보자 - 1 (4) | 2018.09.18 |
Hardware | Raspberry Pi CPU Info screen 구매기 (2) | 2018.08.14 |
Hardware | Safecast bGeigi nano firmware upgrade (0) | 2018.08.12 |
1. firware upgrade
모든 기기는, 그 동작의 기본이 되는 firmware 가 있습니다.
저번에 만들어 봤던 Safecast bGeigie nano 도 firmware 가 있으므로 upgrade 해봅니다.
지금까지 bGeigie nano 에 대해서는 다음 포스트들을 읽어보세요.
* Hardware | Safecast bGeigie Nano 를 조립해 보자 - 1
- http://chocoball.tistory.com/entry/Hardware-Safecast-bGeigie-Nano-1
* Hardware | Safecast bGeigie Nano 를 조립해 보자 - 2
- http://chocoball.tistory.com/entry/Hardware-Safecast-bGeigie-Nano-2
* Hardware | bGeigie Nano 의 battery 를 업그레이드 해보자
- http://chocoball.tistory.com/entry/Hardware-bGeigie-Nano-battery-upgrade
* Hardware | bGeigie Nano 를 이용하여 방사능을 측정해 보자
- http://chocoball.tistory.com/entry/Hardware-bGeigie-Nano-checking-radiation
참고로 firmware upgrade 하기 전에는 1.3.4 입니다.
사용된 환경은 Mac 입니다.
아무래도 unix based OS 이고, arduino / FTDI 사용시 반응이 빠릿빠릿 해서 입니다.
2. FTDI driver
우선 FTDI for mac 드라이버를 인스톨 합니다.
위 링크에서 최신 driver 를 다운로드 받아서 설치합니다.
정식 명칭은 FTDI USB Serial Driver 군요.
3. AVR 설치
아래 링크에서 다운로드 받아서 설치합니다.
오랜만에 Mac 에서 설치작업을 해보는군요.
2013년에 나온게 최신버전인가 보군요.
4. FTDI 연결하기
bGeigie nano 의 중앙 처리장치인 arduino FIO 옆에 pinout 이 있습니다.
firmware 업그래이드를 위해 마련된 FTDI 연결 포트입니다.
신기하게도 알리에서 구입한 FTDI breakout board 의 pinout 과 순서가 완벽히 일치합니다.
RX/TX 도 서로 엇갈리게 되어 있고, Vcc / GND 등 모두 짝이 맞춰져 있습니다.
참고로, firmware update 시의 주의사항 입니다.
NOTE: The Nano power switch MUST be turned OFF before connecting (the Fio board powers from the FTDI cable)!
NOTE: If you have a BLEBee or other wireless module, it MUST be removed before reprogramming, since it shares TX/RX signals!
즉, 전원은 꼭 off 로 해 놓고, FTDI 에서 받는 3.3V 를 이용하라는 것이고,
Bluetooth 용인 BLEBee 모듈을 꼭 제거하고 실행하라는 것 입니다. 그렇지 않으면 TX/RX 가 선점되어서 통신이 시작되지 못합니다.
(삽질 하루 걸림...)
꼭! BLEBee 모듈은 제거!
FTDI 와 연결합니다.
지금까지 여러가지 해봤더니, 어느새 FTDI 를 가지고 있네요?
* Hardware | FTDI Serial Adapter 를 사용해 보자
- http://chocoball.tistory.com/entry/Hardware-FTDI-FT232RL-using
5. 최신 firmware 다운로드 및 upgrade
최신 firmware 를 다운로드 받습니다.
- wget https://github.com/Safecast/bGeigieNanoKit/raw/master/bGeigieNano.hex
파일을 다운로드 받으려고 하면, redirection 되어서 다음 화면의 링크에서 다운로드 받네요.
"bGeigieNano.hex" 가 그 최신 파일입니다.
다음 명령어로 flashing 합니다.
avrdude -DV -p atmega328p -P /dev/tty.usbserial-A50285BI -c arduino -b 57600 -U flash:w:bGeigieNanao.hex:i
USB port 는 각 기기마다 다르게 보일 터이니, ls 명령어로 우선 확인해 보고 맞는 이름을 사용하면 되겠습니다.
저의 경우는 "/dev/tty.usbserial-A50285BI" 였습니다.
짜잔~. 최신 버전인 1.4.2 로 업그레이드 되었습니다.
참고로 Windows OS 에서의 실행 결과 입니다.
나중을 위해 명령문도 기록해 놓습니다.
C:\"Program Files (x86)"\Arduino\hardware\tools\avr/bin/avrdude -CC:\"Program Files (x86)"\Arduino\hardware\tools\avr/etc/avrdude.conf -DV -p atmega328p -PCOM5 -c arduino -b 5700 -U flash:w:bGeigieNano.hex:i
6. 마무리
최신 firmware 로 upgrade 한 다음, microSD 및 GPS reset 을 해 줍니다.
GPS reset 은, microSD 를 뺀 다음, 부팅시켜주는 것이고,
microSD는 FAT 로 포맷하고, 필요한 파일인 "config.txt", 및 "SAFECAST.TXT" 만 root 에 copy 하고 리부팅 하면 됩니다.
FIN
거부감 없이 bGeigie nano 의 firmware 를 업그레이드 해봤습니다.
사실 microSD 카드에 logging 하는 기능이 정상 동작하지 않아, 궁여지책으로 해본 작업이었습니다.
firmware upgrade 를 해도 개선이 안되는 것을 보면, 다른 문제가 있어 보이네요.
국내에 얼마나 많은 분들이 가지고 계실지는 모르겠습니다만, 참고가 되었으면 합니다.
Update 20191228
마지막에 기술했다 시피, microSD 카드에 logging 되지 않는 현상과,
toggle switch 로 mode 를 변경시켜도 mode 가 변하지 않는 문제가 지속되었습니다.
일단, toggle switch 의 접점 문제로 인하여 mode 변경이 되지 않는게 아닌가 하여, 관련 부분 납땜을 다시 정리해 주기로 합니다.
살살 분해 합니다.
좀 많이 튀어 나온 부분이나, 납이 부족하다고 생각되는 부분을 정리해 줍니다.
이 작업 후에도 개선은 되지 않더군요... ㅠㅠ
1년 4개월이 흐른 뒤...
긴 휴가를 맞이하여 다시한번 도전하기로 합니다.
9개월 전에 새로운 firmware 가 올라 왔군요. 버전은 1.4.3.
그 전 버전이 1.4.2 였으니 마이너 업데이트 이긴 하지만, 밑져야 본전 입니다.
Firmware 업데이트 후, 되는군요... 문제가 고쳐졌습니다. ㅠㅠ
CPM 을 표시하면서 logging (microSD 에 기록) 하는 모드가 정상으로 돌아 왔습니다!!!
물론, 단순 측정 모드 (logging 하지 않음) 도 잘 되구요.
이제야 제대로 사용할 수 있으려나 합니다.
'Hardware' 카테고리의 다른 글
Hardware | Arduino 를 DIY 해보자 - 1 (4) | 2018.09.18 |
---|---|
Hardware | Raspberry Pi CPU Info screen 구매기 (2) | 2018.08.14 |
Hardware | Raspberry Pi 3 model B+ unboxing (0) | 2018.08.10 |
Hardware | MAX31865 + PT100 온도센서 조합 (2) | 2018.08.06 |
Hardware | ML8511 UV sensor 를 가지고 놀아보자 (0) | 2018.08.01 |
1. Raspberry Pi 3 의 새버전이다!
2016년에 Raspberry Pi 3 model B 가 나온지 2년이 지난 지금, 3 model B 의 업그레이드 버전인 3 model B+ 가 출시되었습니다.
이 친구들은 2~3년에 한번씩 나올꺼라 하더니만, 실제로 그러네요.
3 model B+ 가 처음 출시당시인 올해 3월에는 도저희 구할 수 없어서 참고 있다가,
이번달에 되어서야 구매했습니다.
정시가는 35 USD 이지만, 아직도 알리에서 구할 수 있는 가장 싼게 40.70 USD 였네요.
* In Stock 2018 New Original Raspberry Pi 3 Model B+ on-board 2.4G & 5G WIFI Cpu Plus 1.4GHz add PoE Raspberry Pi 3 Plus Pi3B+
2. 도착
역시 가격이 좀 되는거라 1주일만에 도착했습니다.
음? RPi 는 명함사이즈 인데 박스가 꽤 크군요.
뽁뽁이 봉투를 제거하면 비닐 포장지가 나옵니다.
그 안에 박스가 이제야 나오는군요. 그렇지만 여전히 큽니다.
Hey~~~! 안녕.
Model B+ 문구가 저를 반겨 줍니다.
버전이 올라갈수록 포장은 점점 더 simple 해지고 있습니다.
아직 RPC 버전 (중국 생산 버전) 은 없는건가?
"Made in United Kingdom" 이라고 박스에는 프린팅 되어 있네요.
저 뒷면의 CMIIT ID 란 무엇일까 찾아 봤습니다.
* CMIIT Wireless Applications
- https://fccid.io/CMIIT-ID-2018AJ2147
우리나라로 따지면 "전파 인증" 정도가 되는듯 합니다.
3. 본체
짜잔~!!!
여느 버전들과 비슷한 모양의 본체가 박스안에서 나왔습니다.
눈에 들어오는 가장 큰 차이점은 CPU 에 붙은 heat spreader 와, 산딸기가 세겨진 WiFi 뚜껑정도가 되겠네요.
"Made in the UK" 되겠습니다.
뒷면도 얼른 확인해 봤습니다. Elpida 의 1G RAM은 여전하네요. 언능 2G 시대가 왔으면... 하고 바래 봅니다.
4. B vs. B+
팔려나갈 Model B 와의 비교샷 입니다.
오른쪽 위에 PoE 점퍼가 추가되었으며, 전원부 근처에 칩이 하나 커졌습니다.
그리고 기판 위의 각 component 들에 대한 라벨링이 사라졌습니다.
밑면 model B 입니다.
메모리 주변 회로가 깔끔해졌습니다.
옆면은 비교할게 없어서 그냥 model B+ 사진입니다.
Network port 생산연도는 18년 16주차인듯 하네요.
5. 전원 ON !
그 사이에 Ubuntu-Mate 18 버전이 새로 나왔고, 새로 구입한 Raspberry Pi 3 model B+ 이니,
OS 를 새로 깔아야 마땅 하나 너무너무너무너무너무너무너무너무너무너무너무너무 귀찮습니다.
CPU (클럭 상승) / Network 정도만 바뀐거라, 기존 OS 도 무리없이 돌아갈 것이라 생각되었습니다.
기존 model B 에 꽂혀있던 microSD를 넣고 전원을 키니 문제없이 Ubuntu-Mate 16 이 잘 돌아갑니다.
Keyboard / HDMI / Wired netowrk 등을 연결했더니만,
화면 좌측 윗쪽에 번개표시가 자꾸 뜨면서, network 을 통한 다운로드 속도가 줄고, 진행이 더디어지는게 느껴집니다.
네... idle 시에는 전기를 적게 먹지만, 로드가 걸리면 전기를 더 많이 처 드시는군요.
위의 표에서 가장 오른쪽이 칼럼이 model B+ 입니다.
* Raspberry Pi
- https://en.wikipedia.org/wiki/Raspberry_Pi
"Under-voltage detected! (0x00050005) 가 작렬하지만, 우선 다른 선택이 없으므로 기존 USB charger 를 그대로 사용합니다.
좀더 안정적으로 전원 공급이 되는 USB Power 이어야 본래의 성능이 나온다 하니,
나중에 좋은걸로 달아줘야겠습니다.
6. OS / firmware upate
가는날이 장날이라고, "apt-get update" 와 "apt-get upgrade" 를 통하여 package update 를 시켜줍니다.
흠흠... 문제 없이 잘 됩니다. (무슨 문제가 생기겠어...)
그 후에, "rpi-update" 를 통하여 Raspberry Pi firmware update 를 시켜줍니다.
모두 끝나고 리부팅해서 확인해 보면, 문제 없이 적용이 되었습니다.
Raspberry Pi 로서는 6번째 라즈베리파이 인지라 뭔가 그리 흥은 많이 안나는군요.
심심하니, dmesg 를 통하여 부팅 시퀀스도 확인해 봅니다.
CPU 는 ARMv7 의 revison 5 로 알고 있었는데, 아직 OS 에서 명확하게 구분해주지 못하나 봅니다.
참고로 CPU 제조는 Sony 라는 사실.
* RPi HardwareHistory
- https://elinux.org/RPi_HardwareHistory
흠흠~~ 그렇구나~~.
FIN
이로써, 1 Model B, 1 Model B+, 2 Model B, 3 Model B 및 이번의 3 Model B+ 까지,
Model B 버전은 모두 사용해보게 되었습니다.
개인 WiKi page 를 운영하고 있어서, CPU 도 좋지만 RAM 좀 얼른 2G로 올려주면 안되겠니?
'Hardware' 카테고리의 다른 글
Hardware | Raspberry Pi CPU Info screen 구매기 (2) | 2018.08.14 |
---|---|
Hardware | Safecast bGeigi nano firmware upgrade (0) | 2018.08.12 |
Hardware | MAX31865 + PT100 온도센서 조합 (2) | 2018.08.06 |
Hardware | ML8511 UV sensor 를 가지고 놀아보자 (0) | 2018.08.01 |
Hardware | MAX31855 + K-type 온도센서 조합 (2) | 2018.07.31 |
1. MAX31865
K-Type 온도 센서를 arduino 와 연결하여 측정하는 MAX31855 은 아래 포스팅에서 가지고 놀아 봤습니다.
* Hardware | MAX31855 + K-type 온도센서 조합
- http://chocoball.tistory.com/entry/Hardware-MAX31855-Ktype
그 외의 온도센서에 대해서는 아래 포스트들을 참고해보세요.
* Hardware | AM2322 Temperature & Humidity Sensor
- http://chocoball.tistory.com/entry/Hardware-AM2322-Temperature-Humidity-Sensor
* Hardware | Arduino 비접촉 온도센서 GY-906 MLX90614
- http://chocoball.tistory.com/entry/HardwareArduinoMLX90614
* Hardware | Arduino BMP280 고도/온도/기압 센서
- http://chocoball.tistory.com/entry/HardwareArduinoBMP280
* Hardware | BME280 sensor
- http://chocoball.tistory.com/entry/HardwareBME280
* Hardware | DS18B20 온도센서
- http://chocoball.tistory.com/entry/Hardware-DS18B20-temperature-sensor
여기서 주의할 점은, MAX31865은 MAX31855와 제일 마지막 앞번의 digit 차이 입니다.
주문시 주의해야 합니다.
* MAX31865 : PT100 / PT1000 용
* MAX31855 : K-type 용
2. MAX31865 구입
AliExpress 에서 검색하면 아래의 링크가 가장 많이 구입한걸로 나옵니다.
바로 주문 넣습니다.
* MAX31865 SPI PT100/PT1000 RTD-to-Digital Converter Board Temperature Thermocouple Sensor Amplifier Module For Arduino 3.3V/5V
3. 도착
다른 센서들보다는 조금 높은 가격입니다.
그러나 업자는 항상 1 USD 미만으로 보내주십니다. :-)
포장은 일반적인 간단한 포장입니다.
Adafruit 제품과 거의 동일하게 만들었습니다.
Chip 을 확대해 봤습니다.
Taiwan 에서 생산된 M31865 라고 인쇄되어 있습니다.
Pin array 와 terminal 을 납땜해 줍니다.
Rref 의 저항은 431 = 430 ohm 입니다.
4. Thermocouple K-Type 구매하기
MAX31865 를 구입하면서, 거의 동시에 온도 센서도 주문에 넣습니다.
* Thermocouple K-Type Thermocouple Thermometer Probe WRNT-03 200mm*1000mm
분명히 K-Type 이라고 적혀 있습니다.
Probe 형이라 길이가 깁니다. 포장도 큼지막 합니다.
뽁뽁이로 잘 쌓여서 도착했습니다.
음? CU50?
이미 MAX31855 글에서도 CU50 에 대해서 적었습니다.
MAX31865 / MAX31855 의 개념이 없었고, K-Type / PT100 / PT1000 개념도 없이 처음 질렀던 온도계 센서 입니다.
더욱 헷갈리게 된 것은, K-Type 이라고 주문한게 CU50 이 도착해, 처음 한동안은 뭐가 문제인지 몰랐습니다.
그 뒤에 센서 및 coverter chip 종류가 다르다는걸 알았죠.
* CU50 / WRNT-03 spec.
뭘 모르면 알때까지 삽질해야 하는 것은 인생의 진리 입니다.
5. PT100 구입하기
MAX31865 는 PT100 / PT1000 용 analog to digital converter 입니다.
PT1000 은 예민하면서 실험실용으로 사용되고 있어서, PT100 을 선택합니다.
* MYLB-0-400C PT100 Type 5mm x 50mm Temperature Controller Thermocouple Probe 2 Meters
점점 진실에 접근하는 느낌이 듭니다.
도착.
포장 문제 없슴.
선은 3 wire 센서 이군요.
흠흠.
색이 다른 터미널끼리는 거의 100 ohm 의 차이를 보입니다.
이는 PT100 의 일반적인 현상이며, 더운 여름에 측정하다 보니 저항값이 조금 높습니다.
이 저항값의 변화를 가지고 온도를 측정하는 것입니다.
동일 선끼리는 거의 0 ohm 입니다.
6. Pinout / Layout
Pinout 에 대해서는 아래 Adafruit 의 링크를 참조해 보세요.
* Adafruit MAX31865 RTD PT100 or PT1000 Amplifier
- https://learn.adafruit.com/adafruit-max31865-rtd-pt100-amplifier/pinouts
MAX31865 | Arduino Micro --------------------------- Vin | 3.3V GND | GND CLK | D13 SDO | D12 SDI | D11 CS | D10 ---------------------------
Layout 입니다.
7. sketch
레퍼런스 소스를 이용해서 기본적인 동작 확인에 들어갑니다.
우선 Library Manager 에서 max31865 를 검색해서 install 해줍니다.
인스톨이 완료되면, "File > Examples > Adafruit MAX31865 library > max31865" 을 선택하여 소스를 로딩합니다.
아래는 sketch 입니다.
/*************************************************** This is a library for the Adafruit PT100/P1000 RTD Sensor w/MAX31865 Designed specifically to work with the Adafruit RTD Sensor ----> https://www.adafruit.com/products/3328 This sensor uses SPI to communicate, 4 pins are required to interface Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit! Written by Limor Fried/Ladyada for Adafruit Industries. BSD license, all text above must be included in any redistribution ****************************************************/ #include// Use software SPI: CS, DI, DO, CLK Adafruit_MAX31865 max = Adafruit_MAX31865(10, 11, 12, 13); // use hardware SPI, just pass in the CS pin //Adafruit_MAX31865 max = Adafruit_MAX31865(10); // The value of the Rref resistor. Use 430.0 for PT100 and 4300.0 for PT1000 #define RREF 430.0 // The 'nominal' 0-degrees-C resistance of the sensor // 100.0 for PT100, 1000.0 for PT1000 #define RNOMINAL 100.0 void setup() { Serial.begin(115200); Serial.println("Adafruit MAX31865 PT100 Sensor Test!"); max.begin(MAX31865_3WIRE); // set to 2WIRE or 4WIRE as necessary } void loop() { uint16_t rtd = max.readRTD(); Serial.print("RTD value: "); Serial.println(rtd); float ratio = rtd; ratio /= 32768; Serial.print("Ratio = "); Serial.println(ratio,8); Serial.print("Resistance = "); Serial.println(RREF*ratio,8); Serial.print("Temperature = "); Serial.println(max.temperature(RNOMINAL, RREF)); // Check and print any faults uint8_t fault = max.readFault(); if (fault) { Serial.print("Fault 0x"); Serial.println(fault, HEX); if (fault & MAX31865_FAULT_HIGHTHRESH) { Serial.println("RTD High Threshold"); } if (fault & MAX31865_FAULT_LOWTHRESH) { Serial.println("RTD Low Threshold"); } if (fault & MAX31865_FAULT_REFINLOW) { Serial.println("REFIN- > 0.85 x Bias"); } if (fault & MAX31865_FAULT_REFINHIGH) { Serial.println("REFIN- < 0.85 x Bias - FORCE- open"); } if (fault & MAX31865_FAULT_RTDINLOW) { Serial.println("RTDIN- < 0.85 x Bias - FORCE- open"); } if (fault & MAX31865_FAULT_OVUV) { Serial.println("Under/Over voltage"); } max.clearFault(); } Serial.println(); delay(1000); }
결과는 아래와 같습니다.
뭔가 많이 이상하군요... 뭐가 문제일까...
8. Jumper !!!
그렇습니다.
Adafruit 의 점퍼 설정을 대충 읽은 결과 입니다.
위의 글에 나와 있듯이, 미세하게 연결된 24 부분의 선을 잘라줘야 합니다.
시키는 대로 했으나 제대로 되지 않았습니다.
처음에는 CU50 으로 삽질하면서 2 wire 설정으로 납땜 했다가, 납 지워줬다가, 3 wire 설정으로 다시 납땜 했다가,
기판이 지저분해진 상태이고, 자주 인두로 지져서 기판 상태가 엉망으로 되면서 고장난게 분명하다는 결론에 도달했습니다.
(모두 합하면 10시간동안 삽질함)
9. 재구매
breakout 보드값이 비싸지만, 모든걸 다 해본 뒤라 새로운 converter 를 구입하기로 합니다.
이번에는 색이 다른것으로 구입합니다.
배송료 합하면 거의 6천원이군요... ㅠ.ㅠ
* 31865 MAX31865 RTD platinum resistance temperature detector module PT100 to PT1000
잘 도착했습니다. 얼른 시험해보고 싶습니다.
점퍼 사시의 간극이 커서 잘 납땜해야 합니다.
Rref 저항은 430 ohm 으로 동일하며, 2 4 사이에 미세하게 연결되어 있는것도 확인했습니다.
커터로 잘 그어서 절단해 줍니다.
두둥!
잘 연결하고 확인해 봅니다.
Aㅏ.....
그렇습니다. 여러 삽질을 하는 동안, 기존 breakout 기판이 고장난 것이었습니다.
그간 삽질하면서 학습한 내용을 토대로 잘 jumper 도 납땜하고, 자를껀 자르고 연결하니 한방에 성공합니다.
애증의 MAX31865 와 PT100 센서 입니다.
찬물 / 상온 물 / 뜨거운 물을 가지고 확인한 동영상 입니다.
calibration 이 필요해 보이지만, 그딴거는 나중에 기회되면 하겠습니다.
여기까지 오는것만 해도 힘들었습니다.
그래프화 시킨 그림입니다.
동작에 무리 없어 보입니다.
FIN
제가 가지고 있는 온도센서 및 converter breakout 보드는 다 확인해 봤네요.
CU50 만 빼고... 이놈은 어떻게 해야 할까요.
'Hardware' 카테고리의 다른 글
Hardware | Safecast bGeigi nano firmware upgrade (0) | 2018.08.12 |
---|---|
Hardware | Raspberry Pi 3 model B+ unboxing (0) | 2018.08.10 |
Hardware | ML8511 UV sensor 를 가지고 놀아보자 (0) | 2018.08.01 |
Hardware | MAX31855 + K-type 온도센서 조합 (2) | 2018.07.31 |
Hardware | SSD1306 에 로고를 세겨보자 (0) | 2018.07.24 |