Arduino Nano 33 BLE Sense

경고

이 보드는 더 이상 지원되지 않습니다. Arduino Nano 33 BLE Sense용 마지막 OpenMV 펌웨어 릴리스는 4.7.0입니다. 이 타겟에 대해서는 추가 펌웨어 업데이트, 버그 수정, 신규 기능이 제공되지 않습니다. 아래 정보는 4.7.0 이하 버전을 사용하는 사용자를 위해 보존됩니다.

Arduino Nano 33 BLE Sense는 Nordic Semiconductor nRF52840을 중심으로 구성된 45 × 18 mm 크기의 Arduino Nano 폼팩터 보드입니다. nRF52840은 64 MHz로 동작하는 FPU 탑재 단일 ARM Cortex-M4로, 256 KB의 내부 SRAM과 1 MB의 내부 플래시를 갖추고 있습니다. BLE는 다이 내장 라디오에서 제공되며, 보드에는 9축 IMU, LPS22HB 기압계, HTS221 / HS3003 온습도 센서, APDS9960 주변광 / 색상 / 근접 / 제스처 센서, MP34DT05 PDM 마이크가 탑재되어 있습니다. OpenMV 펌웨어는 이 모든 것을 MicroPython에서 구동합니다.

Arduino Nano 33 BLE Sense

전체 데이터시트, 사진, 치수는 Arduino Nano 33 BLE Rev2 제품 페이지를 참조하십시오.

주요 특징

  • 64 MHz로 동작하는 FPU 탑재 Nordic nRF52840 Cortex-M4. 256 KB 내부 SRAM1 MB 내부 플래시 탑재.

  • 다이 내장 라디오와 Nordic SoftDevice s140을 통한 Bluetooth LE 5.0.

  • 9축 IMU — Rev 1에서는 LSM9DS1, Rev 2에서는 BMI270 + BMM150. 고정된 imu 드라이버가 부팅 시 둘 다 탐지합니다.

  • LPS22HB 기압계, HTS221 / HS3003 온습도 센서, APDS9960 주변광 / 색상 / 근접 / 제스처 센서, MP34DT05 PDM 마이크.

  • 전원, 프로그래밍, CDC REPL을 위한 Micro USB 커넥터.

  • 표준 Nano 헤더의 22개 사용자 I/O 핀TX/RX, D2D13(디지털), A0A7(아날로그).

핀아웃

Arduino Nano 33 BLE Sense 핀아웃

핀 레퍼런스

핀 이름

참조

기능

TX

3.3 V

UART1 TX

RX

3.3 V

UART1 RX

D2

3.3 V

PWM

D3

3.3 V

PWM

D4

3.3 V

PWM

D5

3.3 V

PWM

D6

3.3 V

PWM

D7

3.3 V

PWM

D8

3.3 V

PWM

D9

3.3 V

PWM

D10

3.3 V

PWM

D11

3.3 V

PWM / SPI0 MOSI

D12

3.3 V

PWM / SPI0 MISO

D13

3.3 V

PWM / SPI0 SCK

A0

3.3 V

ADC / PWM

A1

3.3 V

ADC / PWM

A2

3.3 V

ADC / PWM

A3

3.3 V

ADC / PWM

A4 / I2C_SDA

3.3 V

ADC / PWM / I2C0 SDA

A5 / I2C_SCL

3.3 V

ADC / PWM / I2C0 SCL

A6

3.3 V

ADC / PWM

A7

3.3 V

ADC / PWM

RESET

3.3 V

온보드 RESET 버튼을 누르거나 GND로 당겨 리셋

LED_BUILTIN

D13에 있는 주황색 사용자 LED

LED_RED

RGB LED 적색 채널(액티브 로우)

LED_GREEN

RGB LED 녹색 채널(액티브 로우)

LED_BLUE

RGB LED 청색 채널(액티브 로우)

경고

Nano 33 BLE Sense의 I/O 핀은 3.3 V 전용으로, 5 V를 견디지 못합니다. 5 V를 입력하면 nRF52840이 손상됩니다.

전원 핀

  • VIN — 4.5 – 21 V 입력. 온보드 레귤레이터를 통해 보드에 전원을 공급합니다. USB 5 V 레일에서 다이오드를 거쳐 공급되기도 하므로, USB와 VIN이 서로 역방향으로 구동하지 않고 동시에 존재할 수 있습니다.

  • +5V — 기본적으로 연결되어 있지 않음.

  • +3V3 — 3.3 V 레귤레이터 출력.

  • AREF — 아날로그 기준 핀. 이 보드에서는 nRF52840에 연결되어 있지 않으며, ADC는 항상 3.3 V를 기준으로 합니다.

  • GND — 공통 접지.

Nano 33 BLE Sense는 두 경로 중 하나로 전원을 공급할 수 있습니다:

  • Micro USB — 온보드 레귤레이터에 5 V를 공급합니다.

  • VIN 핀 — 정전압 4.5 – 21 V 전원을 공급합니다.

참고

보드 하단에 VUSB라고 표시된 솔더 점퍼는 +5V를 USB 5 V 레일에 연결합니다. 이를 닫으면 +5V 헤더 핀이 실제로 5 V를 전달하게 됩니다.

참고

온보드 4.5–21 V 스위칭 레귤레이터의 출력에 있는 상시 닫힘 솔더 점퍼를 잘라 레귤레이터를 비활성화할 수 있으며, 이렇게 하면 +3V3의 외부 3.3 V 전원으로 보드에 직접 전원을 공급할 수 있습니다.

복구 및 디버그 핀

  • RESET — 보드 상단에 노출된 패드와 모멘터리 RESET 버튼이 모두 nRF52840의 리셋 라인에 연결되어 있습니다. GND로 당기거나 버튼을 눌러 리셋합니다.

Nano 33 BLE Sense는 Arduino의 표준 더블 탭 리셋을 사용해 Arduino 부트로더로 진입합니다. RESET 버튼을 빠르게 두 번 누르면 보드가 부트로더 모드로 진입하고 OpenMV IDE가 새 펌웨어 이미지를 플래시할 수 있습니다.

nRF52840의 SWD 신호는 보드 뒷면의 도금 패드에 노출되어 있습니다. 모든 디버그 신호는 3.3 V 기준입니다.

온보드 주변장치

LED

Nano 33 BLE Sense에는 실크스크린으로 표시된 LED_RED, LED_GREEN, LED_BLUE 채널을 통해 구동되는 사용자 RGB LED와, D13에 있는 별도의 주황색 LED_BUILTIN이 있습니다. 네 가지 모두 machine.LED를 통해 소프트웨어로 제어할 수 있습니다:

from machine import LED

LED("LED_RED").on()
LED("LED_GREEN").on()
LED("LED_BLUE").on()
LED("LED_BUILTIN").on()

보드의 별도 녹색 전원 LED는 +3.3 V 레일이 올라오면 항상 켜지며 사용자가 제어할 수 없습니다.

카메라 센서

Nano 33 BLE Sense의 OpenMV 펌웨어는 OmniVision OV7670 병렬 CMOS 센서를 지원합니다. 이 보드에는 온보드 이미지 센서가 없으므로, 아래에 나열된 실크스크린 헤더 핀에 OV7670 모듈을 연결하고 csi — 카메라 센서 모듈을 통해 구동하십시오:

import csi

cam = csi.CSI()
cam.reset()
cam.pixformat(csi.RGB565)
cam.framesize(csi.QVGA)
cam.snapshot(time=2000)       # let auto‑exposure settle

while True:
    img = cam.snapshot()

참고

OV7670은 14개의 핀을 사용합니다. 펌웨어는 다음과 같이 핀을 연결합니다:

센서 신호

Nano 33 BLE Sense 핀

D0

D10

D1

TX

D2

RX

D3

D2

D4

D3

D5

D5

D6

D6

D7

D4

HSYNC

A1

VSYNC

D8

PXCLK

A0

MXCLK

D9

POWER

A3

RESET

A2

SCL

A5(I²C 0)

SDA

A4(I²C 0)

OV7670의 I²C 제어 버스는 A5/A4에 노출된 외부 I²C 0과 동일합니다. 센서는 7비트 주소 0x21에 위치하므로, 카메라가 연결된 상태에서는 해당 버스의 사용자 장치가 이 주소를 피해야 합니다.

IMU

9축 IMU는 고정된 imu 모듈을 통해 노출되며, 이 모듈은 보드에 LSM9DS1(Rev 1)이 있는지 BMI270 + BMM150(Rev 2)이 있는지 자동으로 감지하여 통합된 imu.IMU 클래스를 제공합니다. 센서들은 내부 I²C 1 버스(P14 / P15)에 위치합니다:

import time
from machine import I2C, Pin
from imu import IMU

bus = I2C(1, scl=Pin("P15"), sda=Pin("P14"))
sensor = IMU(bus)

while True:
    print(sensor.accel())     # (x, y, z) in g
    print(sensor.gyro())      # (x, y, z) in deg/s
    print(sensor.magnet())    # (x, y, z) magnetometer
    time.sleep_ms(100)

탭 감지나 FIFO 같은 기능에 직접 접근하려면, 해당하는 고정 드라이버(lsm9ds1, bmi270, 또는 bmm150)를 임포트하여 동일한 버스에서 인스턴스화하십시오.

환경 센서

기압계(LPS22HB)와 온습도 센서(Rev 1에서는 HTS221, Rev 2에서는 HS3003)는 IMU와 동일한 내부 I²C 1 버스를 공유합니다:

import time
from machine import I2C, Pin
from lps22h import LPS22H
from hts221 import HTS221

bus = I2C(1, scl=Pin("P15"), sda=Pin("P14"))
lps = LPS22H(bus)
try:
    hts = HTS221(bus)
except OSError:
    from hs3003 import HS3003
    hts = HS3003(bus)

while True:
    print("pressure:    %.2f hPa" % lps.pressure())
    print("temperature: %.2f C"   % lps.temperature())
    print("humidity:    %.2f %%"  % hts.humidity())
    time.sleep_ms(500)

광 / 색상 / 근접 / 제스처

Broadcom APDS9960은 동일한 내부 I²C 1 버스에 위치하며 주변광, RGB 색상, 근접, 제스처 감지를 제공합니다:

import time
from machine import I2C, Pin
from apds9960 import uAPDS9960 as APDS9960

bus = I2C(1, scl=Pin("P15"), sda=Pin("P14"))
apds = APDS9960(bus)
apds.enableLightSensor()

while True:
    print("ambient light:", apds.readAmbientLight())
    time.sleep_ms(250)

마이크

온보드 MP34DT05 PDM 마이크는 audio — 오디오 모듈를 통해 캡처됩니다. 각 버퍼는 부호 있는 16비트 PCM bytearray로 도착하며, DSP를 위해 ulab/numpy로 바로 전달할 수 있습니다:

import audio
from ulab import numpy as np

def loudness(pcmbuf):
    samples = np.array(np.frombuffer(pcmbuf, dtype=np.int16), dtype=np.float)
    rms = np.sqrt(np.mean(samples ** 2))
    if rms > 10000:
        print("Loud!", int(rms))

audio.init(channels=1, frequency=16000, gain_db=24)
audio.start_streaming(loudness)

while True:
    pass

Bluetooth

nRF52840의 Bluetooth LE 5.0 라디오는 Nordic SoftDevice s140에서 동작하며 레거시 ubluepy 모듈을 통해 노출됩니다. 최신 bluetooth / aioble — 비동기 BLE API는 이 빌드에서 활성화되어 있지 않습니다. peripheral(GATT 서버, 광고)과 central(GAP 옵저버 / 스캐너 + 연결) 역할을 모두 사용할 수 있습니다.

단일 Environmental Sensing 서비스와 알림 가능한 온도 특성을 가진 peripheral로 광고합니다. event_handler 콜백은 연결, 연결 해제, CCCD 쓰기 시에 호출됩니다:

from ubluepy import Service, Characteristic, UUID, Peripheral, constants
from machine import LED

def event_handler(event_id, handle, data):
    if event_id == constants.EVT_GAP_CONNECTED:
        LED("LED_GREEN").on()
    elif event_id == constants.EVT_GAP_DISCONNECTED:
        LED("LED_GREEN").off()
        periph.advertise(device_name="Nano 33", services=[svc])

svc = Service(UUID("181A"))                          # Environmental Sensing
char = Characteristic(UUID("2A6E"),                  # Temperature
                      props=Characteristic.PROP_NOTIFY | Characteristic.PROP_READ,
                      attrs=Characteristic.ATTR_CCCD)
svc.addCharacteristic(char)

periph = Peripheral()
periph.addService(svc)
periph.setConnectionHandler(event_handler)
periph.advertise(device_name="Nano 33", services=[svc])

central 역할로 주변의 광고 중인 장치를 스캔합니다:

from ubluepy import Scanner

for entry in Scanner().scan(1_000):                  # 1 second window
    print(entry.addr(), entry.rssi(), "dBm")

전체 API는 ubluepy 레퍼런스를 참조하십시오 — UUID, Service, Characteristic, Peripheral, Scanner, ScanEntry, 그리고 constants 네임스페이스.

버스 레퍼런스

GPIO

실크스크린으로 표시된 핀을 읽거나 구동하려면 machine.Pin을 사용하십시오. 출력은 3.3 V CMOS이며, 핀당 15 mA, 전체 GPIO를 합쳐 25 mA입니다.

from machine import Pin

out = Pin("D2", Pin.OUT)
out.on()
out.off()
out.value(1)

inp = Pin("D3", Pin.IN, Pin.PULL_UP)
print(inp.value())

모든 입력 핀은 에지 전환 시 인터럽트를 발생시킬 수도 있습니다:

def handler(pin):
    print("triggered:", pin)

Pin("D3", Pin.IN, Pin.PULL_UP).irq(
    handler, Pin.IRQ_FALLING | Pin.IRQ_RISING,
)

UART

버스

TX

RX

UART1

TX

RX

실크스크린 이름 TX/RXmachine.UART와 함께 사용하십시오:

from machine import UART

uart = UART(1, baudrate=115200)
uart.write("hello")
uart.read(5)

I²C

버스

SDA

SCL

I2C0

I2C_SDA / A4

I2C_SCL / A5

I2C1

P14

P15

두 버스 모두 핀을 machine.I2C에 명시적으로 전달해야 합니다:

from machine import I2C, Pin

bus0 = I2C(0, scl=Pin("I2C_SCL"), sda=Pin("I2C_SDA"), freq=400_000)
bus0.scan()

bus1 = I2C(1, scl=Pin("P15"), sda=Pin("P14"), freq=400_000)
bus1.scan()

참고

버스 1은 P14/P15에 있는 내부 센서 버스로(사용자 헤더에는 없음), IMU, 기압계, 환경 센서, APDS9960을 담당합니다. 고정된 센서 드라이버가 이를 직접 사용합니다. 사용자 코드도 스캔할 수 있지만 주소는 이미 온보드 센서가 점유하고 있습니다.

SPI

버스

MOSI

MISO

SCK

CS

SPI0

D11

D12

D13

D10

CS 라인은 SPI 주변장치가 구동하지 않습니다. D10을 출력으로 구성하고 전송 전후로 수동으로 토글하십시오:

from machine import SPI, Pin

spi = SPI(0, baudrate=10_000_000)
cs = Pin("D10", Pin.OUT, value=1)   # CS is not driven by the SPI peripheral

cs.value(0)
spi.write(b"hello")
cs.value(1)

참고

D13은 주황색 LED_BUILTIN 역할도 겸하므로, 이 버스에서 SPI를 구동하면 버스 클럭에 맞춰 LED가 깜박입니다.

ADC

nRF52840에는 A0–A7에 노출된 8개의 12비트 ADC 채널(SAADC)이 있으며, 모두 3.3 V 기준입니다. read_u16은 핀의 0–3.3 V 범위에서 0–65535를 반환합니다. 보드의 AREF 핀은 연결되어 있지 않으므로 기준은 항상 3.3 V입니다:

from machine import ADC
import time

adc = ADC("A0")
while True:
    voltage = adc.read_u16() * 3.3 / 65535
    print(voltage)
    time.sleep_ms(100)

PWM

nRF52840은 4개의 PWM 주변장치(PWM0PWM3)를 제공하며, 각각 4개의 채널을 구동하여 총 16개의 하드웨어 PWM 슬롯을 제공합니다. 고정 기능 포트와 달리 이 주변장치는 GPIOTE 매트릭스를 통해 라우팅되므로 어떤 GPIO든 PWM 출력이 될 수 있어 핀과 슬라이스 간 매핑이 없습니다. 이러한 유연성의 대가로 실리콘에 두 가지 제약이 내장되어 있습니다:

  • 한 모듈 내의 네 채널은 단일 주기/주파수를 공유합니다.

  • 각 채널은 자체 듀티 사이클과 극성을 갖습니다.

개념적으로 16개 슬롯은 다음과 같이 보입니다:

모듈

Ch 0

Ch 1

Ch 2

Ch 3

PWM0

듀티

듀티

듀티

듀티

PWM1

듀티

듀티

듀티

듀티

PWM2

듀티

듀티

듀티

듀티

PWM3

듀티

듀티

듀티

듀티

각 행은 하나의 주파수로 동작하며, 한 행의 네 셀은 각각 독립적으로 선택된 핀을 자체 듀티 사이클로 구동합니다. 서로 다른 행은 완전히 다른 주파수로 동작할 수 있습니다.

실크스크린으로 표시된 핀(또는 온보드 LED)을 machine.PWM을 통해 구동하십시오:

from machine import Pin, PWM

pwm = PWM(Pin("D3"), freq=1_000, duty_u16=32768)

경고

자동 할당은 호출당 모듈 하나를 통째로 소비합니다. device=/channel= 키워드 인자 없이 PWM을 생성하면, 드라이버는 첫 번째 빈 모듈을 가져와 핀을 그 모듈의 채널 0에만 바인딩합니다. 해당 모듈의 나머지 세 채널은 유휴 상태로 남아 명시적인 device=/channel=을 통해서만 접근할 수 있습니다. 이 때문에 기술적으로는 슬롯 12개가 여전히 비어 있더라도, 드라이버가 ValueError: all PWM devices in use를 발생시키기 전까지 보조 없는 PWM(Pin(...)) 호출은 4개로 제한됩니다.

PWM을 4개보다 많이 사용하거나 여러 핀에서 의도적으로 주파수를 공유하려면, device(0–3)와 channel(0–3)을 전달하십시오:

# Two PWMs on the same module → forced to share frequency,
# but each gets its own duty cycle.
pwm_a = PWM(Pin("D3"), device=0, channel=0,
            freq=1_000, duty_u16=32768)
pwm_b = PWM(Pin("D5"), device=0, channel=1,
            freq=1_000, duty_u16=16384)

# A third PWM on a separate module, free to pick any frequency.
pwm_c = PWM(Pin("D6"), device=1, channel=0,
            freq=20_000, duty_u16=49152)

듀티 사이클은 duty(0–100%), duty_u16(0–65535), 또는 duty_ns를 받습니다. invert=1을 추가하면 출력 극성이 반전됩니다(액티브 로우 RGB LED에 유용함).

참고

주파수는 모듈별 속성이므로, 한 모듈의 어떤 채널에서든 pwm.freq(new_freq)를 호출하면 모듈 전체에 대해 nrfx_pwm_init이 다시 실행되어, 이를 공유하는 다른 모든 채널이 보는 주파수가 변경됩니다.

참고

허용 주파수는 16 MHz 기본 클럭에서 1/2/4/8/16/32/64/128 프리스케일러와 15비트 주기 카운터로 도출되어 대략 4 Hz에서 5.3 MHz에 걸쳐 있습니다. 드라이버는 가장 가까운 분주기를 자동으로 선택하며, freq()는 실제로 달성 가능한 값이 아니라 요청한 값을 보고합니다.

소프트웨어 비트뱅잉 버스

추가 버스가 필요하면 machine.SoftI2Cmachine.SoftSPI가 모든 GPIO에서 동작합니다.

열 센서(오프보드)

펌웨어에는 외부에 연결된 열화상 카메라용 fir — 열 센서 드라이버 (fir == far infrared, 원적외선) 드라이버가 포함되어 있습니다:

  • MLX90621 — 16 × 4 IR 어레이

  • MLX90640 — 32 × 24 IR 어레이

  • MLX90641 — 16 × 12 IR 어레이

  • AMG8833 — 8 × 8 IR 어레이

모듈을 보드의 I²C 버스에 연결하고 fir.init() + fir.snapshot()으로 프레임을 읽으십시오:

import time
import image
import fir

fir.init()                          # auto‑detects the sensor
clock = time.clock()

while True:
    clock.tick()
    try:
        img = fir.snapshot(x_scale=5, y_scale=5,
                           color_palette=image.PALETTE_IRONBOW,
                           hint=image.BICUBIC,
                           copy_to_fb=True)
    except OSError:
        continue
    print(clock.fps())

fir 드라이버는 I²C 0을 통해서만 센서와 통신합니다. 모듈을 I2C_SCL / I2C_SDA 패드(A5 / A4)에 연결하십시오.

타이밍

time

time 모듈은 블로킹 지연, 단조 증가 틱, 경과 시간 측정을 다룹니다:

import time

time.sleep(1)              # seconds
time.sleep_ms(500)
time.sleep_us(10)

start = time.ticks_ms()
# ...do work...
elapsed = time.ticks_diff(time.ticks_ms(), start)

가상 타이머

machine.Timer는 하드웨어 타이머 슬롯을 소비하지 않고 주기적 또는 일회성 콜백을 예약합니다. 가상(소프트웨어) 타이머를 사용하려면 id로 -1을 전달하십시오:

from machine import Timer

one_shot = Timer(-1)
one_shot.init(period=5_000, mode=Timer.ONE_SHOT,
              callback=lambda t: print("once"))

periodic = Timer(-1)
periodic.init(period=2_000, mode=Timer.PERIODIC,
              callback=lambda t: print("tick"))

주기 값은 밀리초 단위입니다. 슬롯을 중지하고 해제하려면 deinit()을 호출하십시오.

실시간 클럭

machine.RTC는 리셋을 거쳐 벽시계 시간을 유지합니다. nRF52840의 RTC는 온칩 오실레이터에 연결되어 있어 완전한 전원 손실 시에는 유지되지 않으므로, 애플리케이션에 중요하다면 콜드 부팅마다 시간을 설정하십시오:

from machine import RTC

rtc = RTC()
rtc.datetime((2026, 4, 30, 4, 12, 0, 0, 0))   # Y, M, D, weekday, h, m, s, subsec
print(rtc.datetime())

워치독

machine.WDT는 애플리케이션이 멈추면 보드를 리셋합니다. 한 번 시작되면 중지하거나 재구성할 수 없으므로, 메인 루프 안에서 주기적으로 피드하십시오:

from machine import WDT

wdt = WDT(timeout=5_000)   # 5 second window
while True:
    # ...do work...
    wdt.feed()

부팅 및 런타임 정보

펌웨어 업데이트

Nano 33 BLE Sense는 Arduino의 표준 더블 탭 리셋을 사용해 Arduino 부트로더로 진입합니다. RESET 버튼을 빠르게 두 번 누르면 보드가 부트로더 모드로 진입하고 OpenMV IDE가 새 펌웨어 이미지를 플래시할 수 있습니다.

실행 중인 스크립트는 machine.bootloader()를 호출하여 필요할 때 부트로더로 다시 진입할 수 있습니다:

import machine

machine.bootloader()

파일시스템 및 부팅 순서

Nano 33 BLE Sense 펌웨어는 부팅 시 단일 파일시스템을 마운트합니다:

  • 내부 플래시 — 항상 /flash에 마운트되며 작업 디렉터리로 사용됩니다. 기본적으로 main.pyREADME.txt를 담고 있으며, 최초 부팅 시 생성됩니다.

마운트 후, 인터프리터는 /flash에서 스크립트를 실행합니다:

  • boot.py모든 소프트 리셋 시 실행됩니다.

  • main.pyboot.py 직후 콜드 부팅 시에만 실행됩니다.

갓 플래시된 보드에 기본으로 제공되는 main.py는 단순히 사용자 RGB LED의 청색 채널을 하트비트로 깜박이므로(짧은 펄스 두 번, 짧은 간격), 호스트를 연결하지 않고도 펌웨어가 정상적으로 부팅되었는지 알 수 있습니다.

이 보드에서 /flash는 USB 대용량 저장 장치 드라이브로 노출되지 않습니다.

저장 용량

Nano 33 BLE Sense는 다음을 탑재하고 출하됩니다:

  • /flash64 KB FAT 파일시스템, 읽기/쓰기.

Nano 33 BLE Sense 빌드에는 ROMFS가 포함되어 있지 않으므로, Python 모듈을 /flash에 직접 배치하십시오.

소프트웨어 라이브러리

전체 모듈 목록은 라이브러리 색인을 참조하십시오 — Nano 33 BLE Sense 빌드에만 고유한 모듈도 포함되어 있습니다.