2017년 5월 18일 목요일

아두이노 실습] QDSP6064 Bubble Display (4 digit FND)

QDSP6064는 일명 Bubble display라고도 불리는데 4개의 FND가 하나의 패키지에 들어 있는 Common-Cathode 타입의 디스플레이이다.



크기는 크지 않아도 숫자 부분이 뽈록 튀어나와 렌즈 역할을 해 주기 때문에 숫자는 꽤 크게 보인다.


핀 배치는 위의 그림과 같고 Common-Cathode 타입이기 때문에 active high 구동을 하면 된다.


동작을 확인하기 위해 아두이노에는 위의 그림처럼 연결 해 주었다. 그리고 표시되는 카운트를 up/down 하기 위해 두개의 스위치를 추가했다. 우노의 경우 0,1번 핀은 시리얼 포트가 사용하고 있기 때문에 스위치를 추가할 핀이 모자르다. 그래서 아날로그 입력 A0, A1을 디지털 핀으로 사용하였다.


회로도 그림대로 브레드보드에 연결하고 스케치를 실행한 상태이다. 초기 카운트 값이 0이라 QDSP6064에 0000이 표시되고 있다.


동작 동영상은 위와 같다.

위에 사용한 스케치 코드는 다음과 같다.

#define UP A0
#define DN A1

const byte gDgtFont[] = {0x7e,0x30,0x6d,0x79,0x33,0x5b,0x5f,0x72,0x7f,0x7b};
int gCnt = 0;

void setup() {
  int i;

  for (i=0;i<7;i++) pinMode(i+2, OUTPUT);
  for (i=0;i<4;i++) pinMode(i+9, OUTPUT);
  pinMode(UP,INPUT_PULLUP);
  pinMode(DN,INPUT_PULLUP);
  Serial.begin(115200);
}

void loop() {
  dispVal(gCnt);
  checkUpSw();
  checkDnSw();
}

void checkUpSw()
{
  static unsigned long last;
  static boolean prev = HIGH;
  unsigned long now = millis();
  boolean curr;

  if ((now-last)>10) {
    curr = digitalRead(UP);
    if ((HIGH==prev) && (LOW==curr)) {
      gCnt = (gCnt+1)%10000;
    }
    prev = curr;
    last = now;
  }
}

void checkDnSw()
{
  static unsigned long last;
  static boolean prev = HIGH;
  boolean curr;
  unsigned long now = millis();

  if ((now-last)>10) {
    curr = digitalRead(DN);
    if ((HIGH==prev) && (LOW==curr)) {
      gCnt = gCnt-1;
      if (-1 == gCnt) gCnt = 9999;
    }
    prev = curr;
    last = now;
  }
}

void dispVal(int val)
{
  static byte pos = 0;
  static unsigned long last = 0;

  if ((millis()-last)>2) {
    switch(pos) {
      case 0: dispDgt((val/1000),0); break;
      case 1: dispDgt((val/100)%10,1); break;
      case 2: dispDgt((val/10)%10,2); break;
      case 3: dispDgt((val%10),3); break;
    }
    pos = (pos+1) & 3;
    last = millis();
  }
}

void dispDgt(byte dgt, byte pos)
{
  int i;

  for (i=0;i<4;i++) digitalWrite(i+9, HIGH);
  for (i=0;i<7;i++) digitalWrite(i+2, (gDgtFont[dgt]&(1<<(6-i))));
  digitalWrite(pos+9, LOW);
}







2017년 5월 14일 일요일

윈도우 7에서 WannaCry 랜섬웨어 방어법 (Disable SMB v1.0 in Windows 7 for protecting attack from WannaCry ransomeware)



WannaCry 랜섬웨어때문에 난리들이 나서 어떻게 하면 되는가 기사들이 많이 나오고 있는데 전혀 현실적이지 못하게 윈도우 8.1 이상 기준의 방법을 소개하고 있다.

 랜섬웨어 피해 대응법 : 컴퓨터 켜기 전에 인터넷 선부터 뽑아라

사실 국내에서 윈도우 8 계열을 쓰는 사람은 진짜 손으로 꼽을테고 그나마 윈도우 10 아니면 7인데 실질적으로 대다수는 7이라고 본다.  저런 기사에 보면 제어판의 'Windows 기능 켜기/끄기'로 가서 'SMB 1.0/CIFS 파일 공유 지원' 기능을 끄면 된다고 하는데 윈도우 7에서는 그 메뉴로 가 봐도 저 항목이 아예 안 보인다.

해결책은 우선 윈도우 시작메뉴를 누른 다음 '보조프로그램'->'명령 프롬프트'로 간다.


명령 프롬프트를관리자권한으로실행해야하므로마우스오른쪽버튼을클릭해서 메뉴가 나오면 '관리자 권하으로 실행'을 선택해서 클릭한다.


명령 프롬프트를 관리자 권한으로 실행할건지 물어보면 '예'를 선택해준다.


명령 프롬프트 창이 열리면 아래와 같이 정확하게 입력해준다. 주의할 점은 'depend=', 'start=' 뒤에 꼭 한 칸을 띄고 입력해야 한다는 것이다.

sc.exe config lanmanworkstation depend= bowser/mrxsmb20/nsi
sc.exe config mrxsmb10 start= disabled


위와 같이 두 명령 다 '[SC] ChangeServiceConfig 성공'이라는 메시지가 나오면 이제 윈도우를 재시동하면 된다.

이번 랜섬웨어는 SMB v1.0의 취약점을 이용한 공격이기 때문에 SMB v1.0만 비활성화 시켜주면 기본적으로 이번 랜섬웨어의 공격은 막아낼 수 있지만 그래도 안전하게 기사에 있는 것 처럼 MS가 제공하는 패치도 설치해주면 된다.

http://www.catalog.update.microsoft.com/Search.aspx?q=KB4012598







2017년 5월 12일 금요일

BusPirate를 파이선으로 제어하기

수년간 AVR-ISP mkII를 사용해 ATMEL AVR 마이크로 컨트롤러를 프로그래밍 해 오다가, 오늘 처음으로 Bus Pirate를 사용해 보았다. 이 작은 보드는 단순히 마이컴 프로그래머가 아니고 기본적으로 마이컴 주변장치와의 시리얼 인터페이스이다. 보드를 USB에 연결하면 커맨드라인 인터페이스를 가지고 있는 시리얼 포트로 보이는데 이를 통해 핀의 on/off, 전압 측정 뿐 아니고 I2C, SPI, UART 심지어 HD44780 시리즈 LCD 프로토콜까지도 양방향으로 사용할 수 있도록 네이티브하게 지원한다.
HyperTerminal을 사용해 BusPirate와 직접 인터페이스 할 수도 있지만 TeraTerm을 사용하는걸 권장한다. 작은 회로에 전원을 공급할 수 있게 3.3V와 5V 전원을 공급할 수 있고, 만일 전류를 과도하게 소비하면 자동으로 전원 공급을 끊어버린다.
BusPirate는 $30 이하로 구할 수 있는 매우 훌륭한 개발툴이다. 또한 AVRDUDE를 통해 네이티브하게 AVR 프로그래머로 동작할 수 있다. 어셈블리 코드를 작성할수도 있지만 편의성을 위해 거의 항상 C로 프로그램을 작성한다. 나의 레퍼런스 (또는 유사한 작업을 하길 원하는 다른 사람의 레퍼런스)를 위해 윈도우에서 BusPirate로 AVR 마이컴을 프로그래밍 하기 위해 사용하는 가장 쉬운 방법을 포스팅한다. 또한 BusPirate와 연결한 다름 간단한 명령들을 실행하는 파이선 스크립트(파워 서플라이를 켜고 프로그래밍이 끝나면 곧바로 VCC의 전압을 리포팅)를 작성했다.

물론 파이선으로 BusPirate와 인터액션 할 수 있게 해 주는 팬시한 패키지도 있지만 내 방법의 장점은 네이티브 파이선 라이브러리에서 실행된다는 것이다. 이렇게 사용하고 싶으면 WinAVR과 python 3.1을 설치하면 된다. 코드가 파이선 2에서도 동작할것이라고 생각하지만 테스트를 해 보지는 않았다.



BusPirate가 제대로 동작하는지 확인하기 위해 BusPirate의 빌트인 테스트 루틴을 실행하는걸로 시작했다. 상세한 내용은 가이드를 참고하면 된다. 이 테스트는 아래 그림처럼 두 쌍의 핀을 서로 연결해 준 다음, BusPirate를 시리얼 터미널로 연결하고 '~' 명령을 실행하면 된다. 그러면 모든 유용한 정보를 출력해 준다.



다음은 모든 핀들을 동작시키기 위해 마이컴에서 실행될 코드이다. (main.c로 저장) 여기서는 MCU로 ATTiny85를 사용했다. 표준 클럭 설정(내장 RC클럭, 8MHz)을 사용하지만 외부 클럭소스나 크리스털을 사용하기 위해 퓨즈 설정을 바꾸길 원하면 engbedded's handy dandy fuse calculator를 사용해 계산한다.

#define F_CPU (8000000UL)
#include <avr/io.h>
#include <util/delay.h>

int main(void)
{
  DDRB = 255;
  while (1) {
    PORTB ^= 255;
    _delay_ms(500);
  }
}

코드 컴파일 및 MCU 프로그래밍을 위해 항상 같은 폴더에 bash 스크립트를 만들어 더블클릭하면 이전에 컴파일 된 파일들을 삭제하고, main.c를 컴파일 한 다음 BusPirate를 사용해 MCU에 프로그래밍 하게 해 놓았다. 스크립트에서 COM3는 자신의 컴퓨터에 BusPirate에 맞게 바꿔줘야 한다.

@echo off
del *.elf
del *.hex
avr-gcc -mmcu=attiny85 -Wall -Os -o main.elf main.c
avr-objcopy -j .text -j .data -O ihex main.elf main.hex
avrdude -c buspirate -p attiny85 -P com3 -e -U flash:w:main.hex
python up.py
 
비록 프로그래머가 +5V 핀을 통해 MCU에 전원을 공급해 주지만 프로그래밍이 끝나면 전원이 끊어진다.  수동으로 터미널 프로그램을 다시 열어 BusPirate에 재접속 하고 'm' 명령으로 모드를 리셋하고 '9' 명령으로 DIO 모드로 만든 다음 'W'명령으로 전원 출력을 재활성화 시키는 대신 이 모든것을 자동화 하길 원한다. 파이선을 사용하면 이 모든것을 쉽게 할 수 있다. 스크립트의 마지막 줄이 up.py를 호출한다. 이 팬시 스크립트는 전원을 켠 다음 심지어 Vcc라인의 전압까지도 출력 해 준다.  

""" python3 control of buspirate (SWHarden.com) """
import serial

BUSPIRATE_PORT = 'com3'

def send(ser.cmd):
  ser.write(str(cmd+'\n').encode('ascii'))
  for line in ser.readlines():
    print (line.decode('utf-8').strip())

ser = serial.Serial(BUSPIRATE_PORT, 115200, timeout=1)
assert ser.isOpen()
send(ser, '#')
send(ser, 'm')
send(ser, '9')
send(ser, 'W')
send(ser, 'v')
ser.close()
print ("disconnected!")

"burn.cmd"가 실행되면 코드가 컴파일되어 MCU에 프로그래밍 되고 전원공급을 켜고 Vcc의 전압을 알려준다. 출력은 다음과 같다.

C:\Users\scott\Documents\important\AVR\2016-07-13 ATTiny85 LEDblink>burn.cmd

Detecting BusPirate...
**
**  Bus Pirate v3a
**  Firmware v5.10 (r559)  Bootloader v4.4
**  DEVID:0x0447 REVID:0x3046 (24FJ64GA002 B8)
**  http://dangerousprototypes.com
**
BusPirate: using BINARY mode
avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.12s

avrdude: Device signature = 0x1e930b
avrdude: erasing chip
avrdude: reading input file "main.hex"
avrdude: input file main.hex auto detected as Intel Hex
avrdude: writing flash (84 bytes):

Writing | ################################################## | 100% 3.12s

avrdude: 84 bytes of flash written
avrdude: verifying flash memory against main.hex:
avrdude: load data flash data from input file main.hex:
avrdude: input file main.hex auto detected as Intel Hex
avrdude: input file main.hex contains 84 bytes
avrdude: reading on-chip flash data:

Reading | ################################################## | 100% 2.72s

avrdude: verifying ...
avrdude: 84 bytes of flash verified

avrdude: safemode: Fuses OK

avrdude done.  Thank you.

#
RESET

Bus Pirate v3a
Firmware v5.10 (r559)  Bootloader v4.4
DEVID:0x0447 REVID:0x3046 (24FJ64GA002 B8)
http://dangerousprototypes.com
HiZ>
m
1. HiZ
2. 1-WIRE
3. UART
4. I2C
5. SPI
6. 2WIRE
7. 3WIRE
8. LCD
9. DIO
x. exit(without change)

(1)>
9
Ready
DIO>
W
Power supplies ON
DIO>
v
Pinstates:
1.(BR)  2.(RD)  3.(OR)  4.(YW)  5.(GN)  6.(BL)  7.(PU)  8.(GR)  9.(WT)  0.(Blk)
GND     3.3V    5.0V    ADC     VPU     AUX     CLK     MOSI    CS      MISO
P       P       P       I       I       I       I       I       I       I
GND     3.17V   5.00V   0.00V   0.00V   L       L       L       H       L
DIO>
disconnected!

이는 최소한의 케이스 시나리오지만 더 복잡한 태스크를 수행하도록 확장할 수 있다. 예를 들어 모든 커맨드가 단일 파이선 프로그램에서 실행될 수 있다. BusPirate의 수 많은 다른 프로토콜과 통신할 수 있는 능력을 감안하면 별도의 특별한 추가 라이브러리를 설치하지 않고 파이선에서 네이티브하게 제어할 수 있다는건 매우 편리하다.

--
Credit goes to Scott W. Harden




2017년 5월 11일 목요일

라즈베리 파이에서 MQTT를 이용해 ESP8266 제어하기

여기서는 라즈베리 파이, ESP8266(또는 와이파이 쉴드를 붙인 아두이노), PC를 MQTT로 연결해 보도록 하겠다.

이번에 하고자 하는 것은 라즈베리파이가 python을 사용해 PC에 있는 MQTT 브로커에 메시지를 보내면 그 메시지가 ESP8266으로 전달되어 거기에 있는 LED를 제어하는 것이다.

MQTT for PC


MQTT server(브로커)로 사용할 수 있는 옵션이 몇가지 있다. Amazon의 AWS IoT나 AdafruitIO 같은 서비스들은 클라우드 기반의 브로커이다. 각 서비스는 여러가지 장점이 있다. 프로젝트의 필요성에 따라 클라우드 기반 서비스를 사용할 필요가 있을수도 있다.

Mosquitto


자신의 호스트에 브로커를 실행하고 싶으면 오픈소스인 moqsuitto가 훌륭한 옵션이다. 이 브로커는 MQTT를 완전히 지원할 뿐 아니라 pub/sub를 위한 커맨드라인 유틸리티까지 제공한다.

윈도우를 사용하면 mosquitto.org에서 바이너리 인스톨러를 받을 수 있다. 리눅스의 경우 패키지 매니져에서 'mosquitto' 또는 'mosquitto-mqtt'를 찾으면 된다. 맥에서는 homebrew를 사용해 mosquitto를 설치할 수 있다.

* 맥에서 설치한 경우 '/usr/local/sbin'을 PATH에 넣어줘야 한다.

Running Mosquitto


Mosquitto는 디폴트로 1883번 포트를 사용하고 보안기능은 다 꺼져 있다. 처음 시작할 때는 이것으로 충분하지만 결국에는 보안기능을 추가해 줄 필요가 있다.

Mosquitto를 verbose 모드로 실행하길 권장한다. 그러면 출력을 보고 어떤 일이 일어나는지에 대한 아이디어를 얻을 수 있을 것이다.

MacMan:~ james$ mosquitto -v 
1456272937: mosquitto version 1.4.5 (build date 2015-11-09 14:23:46-0800) starting 1456272937: Using default config. 
1456272937: Opening ipv4 listen socket on port 1883. 
1456272937: Opening ipv6 listen socket on port 1883.

Testing Mosquitto


서버 코드를 verbose 모드에서 실행시키고, 추가로 두개의 명령어 프롬프트를 열어준다. 각각의 명령어 프롬프트에서 메시지 전송 테스트를 위해 mosquitto_pub와 mosquitto_sub를 사용할 것이다.

Subscribing to MQTT Topic with Mosquitto


먼저 클라이언트가"debug"라는 토픽을 subscribe하게 해 준다. 첫번째 명령어 프롬프트에서 아래 명령을 입력해 주면 된다.

$ mosquitto_sub -h 127.0.0.1 -i testSub -t debug

* -h : host flag는 mosquitto가 실행되고 있는 서버를 지정. 여기서는 localhost
* -i : identity flag 는 옵션이다. Client ID가 지정되지 않으면 mosquitto_sub가 임의로 만듬
* -t : topic flag 는 subscribe 하려고 하는 topic을 지정. 여기서는 "debug"

서버에서는 topic을 만들지 않았다는걸 주목해라. Topic은 subscriber나 publisher가 처음 지정할 때 만들어진다.

Publish to MQTT Topic with Mosquitto


이제 topic을 기다리는 클라이언트를 가지고 있으므로, 다른 클라이언트가 그 topic을 publish하게 해 본다.

$ mosquitto_pub -h 127.0.0.1 -i testPublish -t debug -m 'Hello World'


이제 첫번째 명령어 프롬프트 화면을 보면 publish된 메시지가 와 있는것을 확인할 수 있다. mosquitto_pub와 mosquitto_sub 명렁 모두 --help 플래그로 확인 해 봐라. 매우 여러가지 유용한 옵션들을 찾아볼 수 있을 것이다.

이제 mosquitto 브로커가 정상 동작하고 있는것이 확인되었으므로 디바이스와 통신을 하게 해 보자.

MQTT for Raspberry Pi (Python)


라즈베리 파이에서 MQTT 메지시를 보내고 받을 수 있는 옵션으로는 mosquitto를 설치하는 것이다. 그러면 라즈베리 파이에서도 동일하게 mosquitto_pub와 mosquitto_sub 명령어를 사용할 수 있다.

다른 방법으로는 자신이 선호하는 언어용 MQTT 라이브러리를 사용하는 것이다. 여기서는 파이선을 사용한다.

Install MQTT for Python


여기서 사용할 MQTT library는 Paho Python Client이다. 오픈소스로 가장 최신의 MQTT를 지원한다.

설치는 간단하다. 먼저 'pip'를 설치하고 나서 아래 명령을 실행한다.

$ pip install paho-mqtt

* 만일 python3를 사용한다면 'pip' 대신 'pip3'를 사용해야 하는걸 잊지 마라.

Example MQTT Python Code for Raspberry Pi


Paho는 MQTT 서버와 통신을 매우 쉽게 만들어 준다.

import paho.mqtt.publish as publish
import time
print ("Sending 0...")
publish.single("ledStatus", "0", hostname="macman")
time.sleep(1)
print ("Sending 1...")
publish.single("ledStatus", "1", hostname="macman")

진짜 간단하다. 스크립트에 서버가 실행되고 있는 호스트 이름(여기서는 'macman'), Topic (여기서는 'ledStatus'), 메시지(여기서는 '0' 또는 '1')만 알려주면 된다.

이 MQTT 튜토리얼 예제에서는 '0'을 보내고 1초 기다린 후에 '1'을 보낸다. 이 동작은 ESP8266에 있는 LED가 잠깐동안 꺼졌다 다시 켜지게 만든다.


이제 RPi가 MQTT 메시지를 보내게 만들었으니 그 메시지를 받도록 만들어 보자. 다음 섹션은 ESP8266이 topic "ledStatus"의 메시지를 받아 실제로 LED를 깜빡이게 만들 것이다.

MQTT for ESP8266


PubSubClient는 아두이노 기반의 MQTT 클라이언트이다. 단지 몇 줄의 코드만으로 매우 쉽게 topic을 subscribe하거나 새로운 topic을 publish할 수 있다.

여기서는 ESP8266을 프로그래밍 하기 위해 아두이노 IDE를 사용했다.

Installing MQTT for Arduino IDE


아두이노 IDE에 PubSubClient를 설치하는건 간단하다. Github에서 수동으로 설치하거나 또는 Arduino Package Installer를 사용할 수 있다.


Arduino ESP8266 and Uno Note


Ethernet 쉴드가 장착된 Uno와 ESP8266간에 스위칭에는 별도의 추가적인 노력이 필요 없다. 라이브러리는 양쪽 모두 잘 동작한다.

Code Example for Arduino MQTT


PubSubCilent에 포함되어 있는 예제는 매우 훌륭하다. 예제에는 특히 MQTT에 대한 ESP8266 코드도 들어있다. 처음 시작할 때 그것들도 꼭 확인해 보기 바란다.

아래는 WiFi에 연결하고 "ledStatus" topic을 subscribe하는 코드이다. ESP8266이 메시지를 받으면 그에 따라 ledPin을 on/off한다.

#include <EEPROM.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <Adafruit_INA219.h>

// Connect to the WiFi
const char* ssid = "Dear John";
const char* password = "password123";
const char* mqtt_server = "macman";

WiFiClient espClient;
PubSubClient client(espClient);

const byte ledPin = 0; // Pin with LED on Adafruit Huzzah

void callback(char* topic, byte* payload, unsigned int length) {
 Serial.print("Message arrived [");
 Serial.print(topic);
 Serial.print("] ");
 for (int i=0;i<length;i++) {
  char receivedChar = (char)payload[i];
  Serial.print(receivedChar);
  if (receivedChar == '0')
  // ESP8266 Huzzah outputs are "reversed"
  digitalWrite(ledPin, HIGH);
  if (receivedChar == '1')
   digitalWrite(ledPin, LOW);
  }
  Serial.println();
}


void reconnect() {
 // Loop until we're reconnected
 while (!client.connected()) {
 Serial.print("Attempting MQTT connection...");
 // Attempt to connect
 if (client.connect("ESP8266 Client")) {
  Serial.println("connected");
  // ... and subscribe to topic
  client.subscribe("ledStatus");
 } else {
  Serial.print("failed, rc=");
  Serial.print(client.state());
  Serial.println(" try again in 5 seconds");
  // Wait 5 seconds before retrying
  delay(5000);
  }
 }
}

void setup()
{
 Serial.begin(115200);

 client.setServer(mqtt_server, 1883);
 client.setCallback(callback);

 pinMode(ledPin, OUTPUT);
}

void loop()
{
 if (!client.connected()) {
  reconnect();
 }
 client.loop();
}


모든 마법은 "callback" 함수 안에서 일어난다. 실제 코드에서는 받은 메시지를 처리하는 함수를 작성하고 싶을 것이다. 여기서는 topic에 대해 한글자 메시지만을 보내기 때문에 이 간단한 코드로 충분하다.

Conclusion


이 MQTT 튜토리얼은 매우 기초적이다. 하지만 라즈베리 파이, ESP8266을 MQTT 브로커가 실행되고 있는 PC에 연결하는데 필요한 최소한의 것을 보여준다.

다음에는 MQTT를 통해 어떻게 더 복잡한 메시지를 보낼 수 있는가를 설명하겠다.




2017년 5월 9일 화요일

아두이노 실습] 푸쉬버튼 long press, short press 판단하기

아두이노에서 푸쉬버튼 스위치의 long press와 short press를 구분하기 위해서는 스위치가 눌린 시간을 기록해 놓은 다음 눌린 스위치가 떨어질 때 스위치가 눌려있던 기간을 계산해 특정 시간보다 짧으면 short press, 길면 long press로 판단 해 주면 된다.



이번 실험을 위해 연결한 회로이다. 13번 핀에 시스템이 동작하는지 확인하기 위한  heartbeat LED를 연결 해 주고 4번 핀에 푸쉬버튼 스위치를 연결해 주었다. 4번 핀의 pinMode로 INPUT_PULLUP을 사용할 것이기 때문에 추가로 저항을 연결할 필요는 없다.

#define SW 4
#define RELEASED 0
#define SHORT_PRESS 1
#define LONG_PRESS  2
#define SW_THRESHOLD  500

#define HB_LED 13

void setup() {
  pinMode(HB_LED, OUTPUT);
  pinMode(SW, INPUT_PULLUP);
  Serial.begin(115200);
}

void loop() {
  static boolean hbLedStat = LOW;
  static unsigned long last;
  int swState = readSw();
 
  if (SHORT_PRESS == swState) {
    Serial.println("SWITCH SHORT PRESSED");
  } else if (LONG_PRESS == swState) {
    Serial.println("SWITCH LONG PRESSED");
  }
  if ((millis()-last) >= 500) {
    digitalWrite(HB_LED, hbLedStat);
    hbLedStat = !hbLedStat;
    last = millis();
  }
}

int readSw()
{
  static boolean prev = HIGH;
  static unsigned long last, pressed;
  boolean curr;

  if ((millis()-last) >= 10) {
    curr = digitalRead(SW);
    if ((HIGH==prev)&&(LOW==curr)) {
      pressed = millis();
      prev = LOW;
    } else if ((LOW==prev)&&(HIGH==curr)) {
      prev = HIGH;
      if ((millis()-pressed) > SW_THRESHOLD) {
        return LONG_PRESS;
      } else {
        return SHORT_PRESS;
      }
    }
    last = millis();
  }
  return RELEASED;
}


코드는 위와 같다. 프로그램이 실행되면 heartbeat LED는 1초 간격으로 계속 깜빡이고 스위치를 눌렀다 떼면 누르고 있던 시간 길이에 따라 short press, long press를 판정해 시리얼 포트로 결과를 출력해 준다.



아두이노 실습] Push button 스위치로 FND 카운트 증가/감소

2개의 push button으로 FND의 숫자 카운트를 up/down 시키는 실습

회로도...여기서는 Common Anode 타입의 FND를 사용


 브레드보드 연결도



4번핀에 연결된 스위치로 숫자 카운트를 up, 5번핀에 연결된 스위치로 숫자 카운트를 down하는 스케치

#define FND_A 13
#define FND_B 12
#define FND_C 11
#define FND_D 10
#define FND_E 9
#define FND_F 8
#define FND_G 7
#define FND_DP 6

#define UP  4
#define DN  5

#define dW digitalWrite
#define ON LOW
#define OFF HIGH

int gCnt = 0;

void setup() {
  for (int i=6;i<14;i++) pinMode(i, OUTPUT);
  pinMode(UP, INPUT_PULLUP);
  pinMode(DN, INPUT_PULLUP); 
}

void loop() {
  readUpSw();  // UP 스위치 처리
  readDnSw();  // DOWN 스위치 처리
  dispFnd(gCnt);
}

void dispFnd(int val)
{
  static int prevVal = -1;

  if (prevVal != val) {   // FND에 표시할 값이 바뀐 경우에만 표시를 변경
    switch (val) {
      case 0:
        dW(FND_A, ON); dW(FND_B, ON); dW(FND_C, ON); dW(FND_D, ON); dW(FND_E, ON); dW(FND_F, ON); dW(FND_G, OFF);
        break;
      case 1:
        dW(FND_A, OFF); dW(FND_B, ON); dW(FND_C, ON); dW(FND_D, OFF); dW(FND_E, OFF); dW(FND_F, OFF); dW(FND_G, OFF);
        break;
      case 2:
        dW(FND_A, ON); dW(FND_B, ON); dW(FND_C, OFF); dW(FND_D, ON); dW(FND_E, ON); dW(FND_F, OFF); dW(FND_G, ON);
        break;
      case 3:
        dW(FND_A, ON); dW(FND_B, ON); dW(FND_C, ON); dW(FND_D, ON); dW(FND_E, OFF); dW(FND_F, OFF); dW(FND_G, ON);
        break;
      case 4:
        dW(FND_A, OFF); dW(FND_B, ON); dW(FND_C, ON); dW(FND_D, OFF); dW(FND_E, OFF); dW(FND_F, ON); dW(FND_G, ON);
        break;
      case 5:
        dW(FND_A, ON); dW(FND_B, OFF); dW(FND_C, ON); dW(FND_D, ON); dW(FND_E, OFF); dW(FND_F, ON); dW(FND_G, ON);
        break;
      case 6:
        dW(FND_A, ON); dW(FND_B, OFF); dW(FND_C, ON); dW(FND_D, ON); dW(FND_E, ON); dW(FND_F, ON); dW(FND_G, ON);
        break;
      case 7:
        dW(FND_A, ON); dW(FND_B, ON); dW(FND_C, ON); dW(FND_D, OFF); dW(FND_E, OFF); dW(FND_F, ON); dW(FND_G, OFF);
        break;
      case 8:
        dW(FND_A, ON); dW(FND_B, ON); dW(FND_C, ON); dW(FND_D, ON); dW(FND_E, ON); dW(FND_F, ON); dW(FND_G, ON);
        break;
      case 9:
        dW(FND_A, ON); dW(FND_B, ON); dW(FND_C, ON); dW(FND_D, ON); dW(FND_E, OFF); dW(FND_F, ON); dW(FND_G, ON);
        break;
    }
    prevVal = val;
  }
}

boolean readUpSw()
{
  static boolean prev = HIGH;
  static unsigned long last;
  boolean curr;

  if ((millis()-last)>10) {
    curr = digitalRead(UP);
    if ((HIGH==prev)&&(LOW==curr)) {   // UP SW가 눌린 시점
      gCnt = (gCnt+1)%10;
    }
    prev = curr;
    last = millis();
  }
}

boolean readDnSw()
{
  static boolean prev = HIGH;
  static unsigned long last;
  boolean curr;

  if ((millis()-last)>10) {
    curr = digitalRead(DN);
    if ((HIGH==prev)&&(LOW==curr)) {  // DOWN SW가 눌린 시점

      gCnt = gCnt-1;
      if (-1 == gCnt) gCnt = 9;
    }
    prev = curr;
    last = millis();
  }
}