레이블이 Arduino인 게시물을 표시합니다. 모든 게시물 표시
레이블이 Arduino인 게시물을 표시합니다. 모든 게시물 표시

2025년 3월 20일 목요일

ESP8266 Digital Input Pin의 불편한 진실

1. 믿었던 놈인데...

ESP8266은 2017년도 부터 사용했기 때문에, 7년 정도 사용해 왔었다. 그동안 여러 종류의 내 프로젝트에 사용한 MCU이고, 이에 따라서 신뢰도가 상당히 높았다. 물론 순수한 ESP8266으로 사용하지는 않고 Wemos D1 mini 계열(정확하게는 clone)을 사용해왔다. 

[ Wemos D1 mini ]



2. 발명품 때문에...

아직 중학생인 아들의 발명품 경진대회 출품작 때문에 최근에 다시 D1 mini를 사용했다. 발명품은 작은 OLED와 푸시 버튼을 2개 가지는 구조이었다. 푸시 버튼 입력을 어떻게 구성할까 생각하다가 그냥 Pull-Down 구조로 구성하기로 하였다.

[ 2개 푸시 버튼과 Pull-down 회로 ]

3. 시작은 쉬웠으나...

OLED야 자주 사용하던 모듈이었기 때문에 별 어려움 없이 회로를 구성하고, 기존 개발해 놓았던 소스코드를 이용하여 나름 쉽게(?) 한글을 출력하였다. 


푸시 버튼은 2개의 Input Pin을 이용하여 데이터를 읽어서 HIGHT, LOW 상태를 체크 하였는데, 문제는 입력신호가 다음번 버튼을 누를때 올라오거나, 한번 눌렀는데 2회가 눌리는 경우, 버튼 근처에 손가락을 가져갔는데도 동작하는 등 여러가지 문제가 나왔다. 사실 Wemos D1 mini의 여러 버전의 Clone을 사용해 보았는데, 이런 경우는 사실 처음이었다.  

4. 의심은 또 다른 의심을...

손쉽게 개발이 끝날줄 알았는데, 버튼이 이상하게 동작해서 먼저 작성한 코드를 의심해 보았다. 이리저리 조건을 더 넣어 보기도 하고, 분기를 좀더 세분화 하기도 해보았지만 해결이 되지 않았다.  그래서 처음으로 푸시 버튼의 물리적 고장을 의심해 보았다.  푸시 버튼을 오래 사용하다 보면은 버튼이 눌려진 채로 안 올라오는 경우도 있고, 또는 눌렀지만 실제적으로는 전기적으로 연결이안되는 경우가 있어서 이다. 열심히 테스트를 가지고 버튼을 눌러 보면서 테스트 하였지만, 특이점을 발견하지 못하였다. 한 백번쯤 테스트 해보고 물리적 이상은 없다고 판단 하였다. 
 그 다음으로 Pull-down 회로를 의심하기 시작했다. 보통 Pull-down 회로를 추가하는 이유는 버튼의 상태가 floating 현상을 가지는 경우가 있어서 이를 안정화하기 위하여 사용한다.  사실 Pull-down 회로가 어려운 것도 아니고 간단하게 저항하나 걸치면 되는 것이라 문제될 부분이 없어 보였는데, 이것도 확실하게 하기위하여 테스터기를 또 열심히 찍어 보았다. 단락도 테스트하고, 저항도 테스트 하며 역시 문제가 없다는 것을 확인하였다. 15K 저항이 너무 낮은 값이어서 Pull-down이 동작을 잘 하지 않을 수도 있다는 글을 어디선가 보아서 100K 저항으로 변경하여 테스트 하였지만 역시 증상은 동일하였다.

 다시 원점으로 돌아와서 소스 코드를 확인하였다. 수십년의 개발 경험을 가지고 있지만, 노안과 집중력 감소로 아무래도 실수는 할 수 있다는 생각으로 열심히 살펴보았다. 

이미  독자분들은 결론을 알겠지만, 이러한 의심들은 전혀 소용이 없었다. 어떠한 노력을 해도 문제가 해결되지 않았다. 

5. 검색을...

결국 구글신의 도움을 받아 보기로 하였다. 구글링한지 30분도 안되어서 아주 아주 흥미로운 내용을 발견하였다. 


결론은 Wemos D1의 짝퉁 버전들은 Digital Input Pin을 이용하여 데이터를 읽을때 편차가 있다는 것이다. 이번 발명품은 2개의 Digital Input Pin을 사용하는데 하나는 큰 문제점이 없었는데, 다른 한개의 Pin이 문제를 일으키고 있었다. 

6. 고생의 끝은...

결국 캐패시터 하나로 해결되었다. 단순히 Digital Input Pin 과 GND 사이에 캐패시터를 연결하면 된다.  세라믹 캐패시터는 극성이 없기 때문에 아무 방향으로 연결해도 문제없다.


0.1uF 세라믹 캐패시터 ]

캐패시터를 추가하니 2개의 푸시 버튼 모두 내가 원하는 형태로 동작하였다.  뭐랄까.. 쾌적해진 느낌이 되었다. 


7. 왜 캐패시터를 사용하는가?

커패시터를 사용하는 이유는 대략 느낌상으로 알고 있었지만, 구글링을 통하여 찾은 내용을 한번더 정리해본다. (여기에 정리해 두면 나중에 찾기 쉬우니....)

- 디지털 신호 안정화(노이즈 필터링) : Arduino 계열의 Digital Input Pin은 전압의 빠른 변화를 감지할 수 있는데, 외부 환경이나 전자기적 간섭으로 인해 신호에 노이즈가 발생할 수 있다. 이에 따라서 의도 하지 않은 상황에서 Input Pin에 HIGH 또는 LOW가 걸릴수 있다.  캐패시터를 연결하면 빠른 전압 변화를 완화하고, 신호가 더 안정적으로 들어오게 된다.

- 디바운싱(Debouncing) : 버튼이나 스위치와 같은 입력 장치가 연결된 경우, 버튼을 눌렀을 때 물리적인 특성으로 인해 여러 번 신호가 변화하는 현상이 발생할 수 있다. 이 현상을 디바운싱이라고 하는데, 캐패시터를 추가하면 신호의 변화를 부드럽게 만들어 여러 번의 잘못된 입력을 방지할 수 있다. 

* 보통 스위치 신호를 안정화 하기 위해서는  0.1uF 정도의 캐패시터를 사용한다.  용량이 커지면 스위치의 반응이 느려지고, 용량이 커지면 스위치의 반응이 민감해지는데, 너무 작은 용량의 캐패시터를 사용하면 노이즈 필터링 효과가 줄어들기 때문에 추천하지 않는다.  


다른 사람들이 나처럼 고생하지 않기를 바라면서 글을 적었다. 
May the force be with you. 

2019년 7월 14일 일요일

ESP로 오실로스코프를 만들자 - #5 ( 성능편)

1. 오실로스코프는 만들만큼 만들어 보았다.

 비교적 저렴한 부품을 이용하여 오실로스코프를 참 여러 종류 만들어 보았다.  내 기준의 필요성으로만 따져보면 ESP8266버전으로도 충분하지만 지인을 위하여 제작하다 보니 여러 버전을 만들게 되었다.  이번 버전을 마지막으로 필요성이 대두되지 않는한 더이상의 업그레이드 버전은 제작하지 않을 예정이다.

2. 마지막 버전은 무엇이 다른가?

 이전버전과 비교하여 논리적으로는 크게 차이가 없지만, 물리적으로 차이가 있다. 그것중 하나가 분압 저항이 소켓 방식이다.  필요에 따라 분압의 정도를 조절할 수 있다는 것이다. 사용자의 선택에 따라서 1/10, 1/20, 1/50 등의 분압 저항을 쓸수 있도록 제작하였다.

먼저 분압저항세트의 크기를 줄이기 위하여 이번에는 칩저항을 사용하였다. 물론 크기적인 문제도 있엇지만 칩저항이 가격적인 장점이 있어서 앞으로 지속적으로 사용할 예정이다. (칩저항이 3/4W 기준으로 일반 저항의 1/4W 보다 저렴하다)

칩저항에 대한 내용은 아래의 링크 참조
[칩 저항애 대하여 알아보자 ]

먼저 앞면은 아래의 사진 처럼 배치하고,
[ 분압 저항의 앞면 ]

뒷면은 아래와 같이 배치하고,
[ 분압 저항의 뒷면 ]
전선을 연결하고, 다리를 납땜하였다.

그리고 오실로스코프쪽에는 소켓을 배치하여 필요에 따라 꼽았다가 뺏다가 할 수 있도록 제작하였다. 분압저항세트는 3종류를 준비하였으며, 필요에 따라 얼마든지 분압 비율을 조절하여 제작하고 설치가 가능하도록 하였다. 이미 만든 세트는 오류를 방지하기 위하여 최대 내압을 프린트하여 붙였다.

[ 소켓 방식으로 제작 하여 탈착 가능 ]


예전에 ADC Chip(ADS7888)을 힘들게 납땜 하였는데, 그나마 이번에는 어뎁터를 구매하여 납땜하였다. 어뎁터 구매 이전에는 납땜 성공 확률이 30% 였는데, 어뎁터 사용으로 100% 성공하였다.
[ 어뎁터를 이용하여 1차 남땝 (초록색) ]



또하나의 특징이 바로 프로브 커넥터 부분이다. 이전까지의 모델은 실용적이긴 하나 커넥터 부분이 워낙 작고, 불안하여 지인은 늘 개조하여 사용하고 있는 점을 감안하여, 이번에는 바나나잭을 이용하여 탈착이 가능하게 하였다.

[ 색상도 맞춘 바나나잭 ]


3. 만드는 과정


 일단 모듈화 한 부품들이 배치될 자리를 살짝 올려 본다.
[ 이렇게 자리 잡으면 될 듯 ]

자리를 일단 잡고 나서 납땜을 했다. 손이 크다 보니 생각보다 어렵긴 했다.

[ 납땜을 이리 저리 하고 ... ]

전원 모듈은 핸드폰에서 뜯은 배터리를 사용하기로 했다.
(사실 새 배터리가 아니여서 생각보다 사용 시간이 짧다는 후기를 들었다. ㅠㅠ)
[ 배터리는 애플 정품? ㅋㅋ ]

기존에 사용하던 스위치도 오작동이 너무 많아서, 이번에는 스위치도 바꾸었다.
[ 스위치의 변신.. 단가가 높을 뿐이다 흐흐 ]

[ 분압 저항이 교체가 쉽도록 배치했건만.. 현실은..ㅠㅠ ]

4. 다 만든 모습

 이전 버전과 비교할때 전면부의 외형은 크게 다를 바가 없다.  두께가 좀 낮아졌을 뿐이다. 그외에 소프트웨어적으로 변경된 부분이라면 분압 저항에 따른 비율 변경 기능이 추가 되었을 뿐이다.

[ 선물용 버전 ]



동작하는 화면을 동영상으로 찍어보았다.



5. 끝으로

 늘 끝없는 도전으로 인생을 살고 있는 나에게 오실로스코프는 새로운 도전이었다. 기성화된 제품에 비교하여 턱없이 부족한 성능일지는 몰라도, 기능을 필요에 따라 특화가 가능하다는 점이 매력이 있다. (필요하면 해당 부분을 코딩하면 되니까..)
 글로는 소개하지 않았지만, 이 오실로스코프의 경우 UDP 통신을 이용한 리모콘이 있다. 오실로스코프의  기능을 리모콘 하나로 사용 가능하다.   로터리인코더를 사용한 버전인데 기회가 되면 따로 소개해야 겠다.



2019년 3월 21일 목요일

NFC를 이용한 선불 교통 카드 잔액 조회기

1. 딸래미(王)의 귀환

 아직 초등학생인 딸래미가 있다. 집에서 먼(대중교통으로 40분) 학원을 다니게 되었는데, 셔틀도 없다. 학원에 갈때는 와이프가 데려다 주지만, 올 때는 내가 퇴근하면서 학원으로 가서 딸래미를 데리고 집으로 온다. 하지만 나도 직딩이라 항상 갈 수는 없는 상황이었다.  딸래미의 귀환이 걱정되기도 하고, 만일의 사태를 위하여 딸래미에게 선불 교통 카드를 사 주었다.

[ 하트빵빵 귀여운 토끼 버전의 교통카드 ]
한두번 사용해보게 했지만, 잔액을 기억 못하는 상황이라, 집에서도 손쉽게 잔액이 확인 가능하도록 리더를 만들어야 했다.

2. 모듈의 귀환

 이번에 제작을 위하여 이전에 사용한 모듈들을 귀환시켰다.  예전의 포스트에 사용한 PN532 모듈을 제일 먼저 귀환 시키고,

[ PN532를 소개한 포스트 바로 가기 ]

[ PN532 ] 

그 다음 OLED 및 ESP8266을 귀환 시켯다.
[ OLED + ESP8266을 이용한 오실로스코프 바로가기 ]


[ OLED ]


3. 제작 결과물 소개

 일단 결과물을 소개하면 아래의 이미지와 같다.
[ 나름 투명 아크릴 상판 ]

[ 측면에서 찰칵. 이번엔 배터리 없이 설계 ]

[ 전원을 연결하면 카드를 태깅하라고 궁시렁 ]

[ 좀더 신선하게 고속버스 안에서 촬영 ]

[ 카드를 가져다 대면 잔액이 표시됨 ]


수준이 좀 낮은 상태로 촬영 되었지만, 동작은 아래의 모습으로 진행된다.




4. 제작 방법 소개

납땜할 포인트가 별로 없다. 기껏해야 20-30 포인트 정도된다.  회로도 까지라고 말할 필요도 없고 아래와 같이 연결하면 된다.
[ 심플한 연결도~ ]

나는 모듈들은 모두 소켓 방식을 사용하기 때문에, ESP8266은 기판의 뒷면에 꼽을수 있는 형태로 제작되었다. NFC 나 OLED 모듈은 기판의 앞면에서 꼽는다.
   
[ 급제작으로 인한 케이블 배선의 엉성함은 참아 주시라~ ]

소스코드는 전체 공개이지만, 라이브러리는 이 포스트에 기록하지 않는다.
먼저 필요한 라이브러리는 2개이다.

PN532관련된 라이브러리는 아래의 링크에서 다운로드 가능하다.
https://github.com/adafruit/Adafruit-PN532


OLED에 한글을 출력하는 과정은 나름 껄끄롭지만, 그 전체 과정 및 라이브러리는 아래의 링크에서 다운로드 가능하다.
http://andy-power.blogspot.com/2018/08/oled.html

대략 이번 선불 교통카드 잔액 조회 기능을 만들기 위해서는 100행 정도를 코딩했으며, 코딩된 전체 내용은 아래와 같다.
/* ------------------------------------------------------
  T-Money Checker
  +-------- GPL License --------------------------------+
  do not remove contact & url
  contact :  terminal0070@gmail.com
  blog :  https://andy-power.blogspot.com/
  -------------------------------------------------------- */
#include <Arduino.h>
#include "Adafruit_PN532.h"
#include "Display_SSD1306.h"
#include "HanDraw.h"
#define OLED_RESET -1 //  S/W 리셋..사용하지 않음

// PN532 V3 SPI모드로 사용하기 위해서는 모듈의 스위치를 변경하고 VCC,GND 포함하여
// 6개의 연결이 필요하다.
// ESP8266 기준으로 정의
#define PN532_SCK  (D5) //
#define PN532_MOSI (D7) //
#define PN532_SS   (D3) //
#define PN532_MISO (D6) //

// I2C 모드를 이용하기 위해서는 아래 2 연결이 필요하다.
#define PN532_IRQ   (2// Not Use
#define PN532_RESET (0// Not connected by default on the NFC Shield

//--------------------------------------------------------------
// Global variables
//--------------------------------------------------------------
Display_SSD1306 Oled(OLED_RESET); // 화면 표시용 객체
Adafruit_PN532 nfc(PN532_SS);     // NFC 객체

//--------------------------------------------------------------
// Setup function
//--------------------------------------------------------------
void setup() {
  Serial.begin(115200);
  delay(50);

  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH); // 내장 LED 끄고
  Serial.println("Start NFC");

  nfc.begin();
  uint32_t versiondata = nfc.getFirmwareVersion();
  if (! versiondata) {
    Serial.print("Didn't find PN53x board");
    while (1); // PN532 못찾은 경우에는 다시 부팅해 버린다.!! (찾을때 까지 리부팅)
  }
  // 제대로 연결되었으면 정보를 출력한다.
  Serial.print("Found chip PN5"); Serial.println((versiondata >> 24) & 0xFF, HEX);
  Serial.print("Firmware ver. "); Serial.print((versiondata >> 16) & 0xFF, DEC);
  Serial.print('.'); Serial.println((versiondata >> 8) & 0xFF, DEC);

  // Oled I2C 방식으로 연결하고, 주소는 0x3c
  Oled.begin(SSD1306_SWITCHCAPVCC, 0x3C, false);

  // Callback 함수를 설정해 주면 필요시 호출 하여 사용함.
  HanDraw.begin(12,  
    [](void) { Oled.clearDisplay(); },// 화면 지우는 콜백함수
    [](int16_t x, int16_t y, uint16_t color) {// 1 픽셀을 그리는 콜백 함수
      Oled.drawPixel(x, y, color);   },
    [](void) { Oled.display(); } //  메모리에서 Display 표시 버퍼까지 보내는 함수
   );

  // configure board to read RFID tags
  nfc.SAMConfig();
  Serial.println("Waiting for an ISO14443A Card ...");
  HanDraw.display(); // 사실상 Oled.display() 동일....
  delay(2000);

  HanDraw.clear();
  HanDraw.setFontSize(16);
  HanDraw.drawString(1, 13, "선불 교통 카드를");
  HanDraw.drawString(1, 33, "태그해 주세요");
  HanDraw.display(); // 사실상 Oled.display() 동일...
}

//--------------------------------------------------------------
// loop function
//--------------------------------------------------------------
void loop() {
  uint8_t success;
  uint8_t responseLength = 64;
  success = nfc.inListPassiveTarget();
  if (success) {
    Serial.println("Found something!");
    // T-money 카드번호, 날짜등을 읽기 위한것
    // OLED에는 표시하지 않음
    uint8_t cardInfo[responseLength];
    uint8_t cardNumSize = 0;
    uint8_t selectApdu[] = { 0x00, 0xA4, 0x00, 0x00, 0x02, 0x42, 0x00 };
    success = nfc.inDataExchange(selectApdu, sizeof(selectApdu), cardInfo, &responseLength);
    if (success) {
      Serial.print("responseLength: "); Serial.println(responseLength);
      nfc.PrintHexChar(cardInfo, responseLength);
      if (responseLength >= 24) {
        Serial.print("card number : "); nfc.PrintHexChar(cardInfo + 8, 8);
        Serial.print("card date : ");   nfc.PrintHexChar(cardInfo + 21, 4);
      }
    }

    // 잔고 조회용
    // 조회가 완료되면 OLED 표시
    uint8_t balance[responseLength];
    uint8_t balanceApdu[] = { 0x90, 0x4C, 0x00, 0x00, 0x04 } ;
    success = nfc.inDataExchange(balanceApdu, sizeof(balanceApdu), balance, &responseLength);
    if (success) {
      Serial.print("responseLength: "); Serial.println(responseLength);
      nfc.PrintHexChar(balance, responseLength);
      if (responseLength >= 4) {
        char fpsbuf[32] = ""; // 숫자를 문자열로 바꾸어 화면에 출력할때 사용됨
        uint32_t money = balance[0] * 256 * 256 * 256 +  balance[1] * 256 * 256 +  balance[2] * 256 + balance[3];
        HanDraw.clear();
        HanDraw.drawString(2, 7, " 교통 카드 잔액 ");
        HanDraw.drawString(2, 25, "------------------");
        dtostrf((float)money, 10, 0,   fpsbuf);
        HanDraw.drawString(2, 43, fpsbuf);
        HanDraw.display();
        delay(2000);
        Serial.print("money : "); Serial.println(money);
      }
    }
  }
  else {
    HanDraw.clear();
    HanDraw.drawString(1, 13, "선불 교통 카드를");
    HanDraw.drawString(1, 33, "태그해 주세요");
    HanDraw.display(); // 사실상 Oled.display() 동일...
  }
  delay(100);
}


-2024-03-11 추가
위의 코드는 일반적인 선불 교통카드에 대한 조회 기능이다. 휴대폰의 NFC 교통카드 번호 확인은 위의 코드로 불가능하다.  nsmaster 님이 알려주신 아래의 APDU로 휴대폰의 교통카드 번호가 확인하다고 댓글을 달아 주셨다. (감사합니다.)
{0x00, 0xA4, 0x04, 0x00, 0x07, 0xD4, 0x10, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00} 
집안에 안드로이드폰이 없어서 아직 확인은 못했다. 

5. 끝으로

 사실 일전에 다 연구한 모듈과 내용을 다시금 정리하고 디스플레이를 추가한 결과물이다. 조회기만을 만드는 것은 당일 저녁 딸래미를 학원에서 집까지 데려오고 나서 시작해서 그날 잠자기 전에 다 만들었다. (수 시간 정도 걸림)  예전에 NFC 모듈 연구 당시 선불교통카드에 APDU 방식으로 커멘드를 보내고 받아야 하는데, 보내야하는 커멘드를 어느 자료에서도 찾을 수가 없어서 정말 고생했다.  이 포스트에서는 관련 커멘드는 1행의 소스코드로 공개되었지만, 이 1행의 코드는 수일간 고생해서 알아낸 코드이다.  연구는 끝이 없고 시간은 적으며, 흰머리는 너무나도 빠르게 늘어간다. ㅠㅠ


ESP8266 Digital Input Pin의 불편한 진실

1. 믿었던 놈인데... ESP8266은 2017년도 부터 사용했기 때문에, 7년 정도 사용해 왔었다. 그동안 여러 종류의 내 프로젝트에 사용한 MCU이고, 이에 따라서 신뢰도가 상당히 높았다. 물론 순수한 ESP8266으로 사용하지는 않고 Wemos...