- 記事公開日
抵抗器を測定できる小型チェッカーを自作
「ブレッドボードで回路を組んだあと、抵抗を片づけるのが面倒すぎる!」
ということで、リード付き抵抗器を手軽に測定できる小型チェッカーを作ってみました。
スイッチをONにして抵抗を差し込むと、すぐに値が表示されます。

ただし、この表示値はあえて正確にはしていません。
たとえば「10.1kΩ」や「9.9kΩ」は、どちらも「10kΩ」と表示するようにしています。
これは、このチェッカーが正確な抵抗値を測定するためのものではなく、「どの袋に戻すか」を素早く判断するためのものだからです。
あえて一般的な値に丸めることで、細かい誤差を気にせず、直感的に仕分けできるようにしました。
使用部品


- ATTINY1604-SSN
- Miniブレットボード/ZY-55 - AliExpress
- OLEDディスプレイ/0.91inch
- CR2032コイン電池ホルダー
- 横型スライドスイッチ
- ピンヘッダ/4Pin
- XHコネクタ/3Pin(UPDI書き込み用)
- 抵抗/10kΩ
- 積層セラミックコンデンサ/0.1μF
- 銅箔テープ
回路図
プログラム
#include <Wire.h>
#include <SSD1306Ascii.h>
#include <SSD1306AsciiWire.h>
#include "fonts/allFonts.h"
#define OLED_ADDR 0x3C
#define RREF 10000UL
#define ADC_PIN PIN_PA7
SSD1306AsciiWire oled;
// =====================================
// 表示位置調整
// =====================================
const int8_t CENTER_OFFSET = +16;
// =====================================
// ADC / フィルタ設定
// =====================================
const uint8_t ADC_SAMPLES = 15;
const uint8_t ADC_AVG_START = 4;
const uint8_t ADC_AVG_END = 10;
const uint16_t ADC_SAMPLE_DELAY_US = 250;
const uint8_t RX_FILTER_NUM = 1;
const uint8_t RX_FILTER_DEN = 4;
const uint16_t LOOP_DELAY_MS = 120;
const uint16_t OPEN_ADC_TH = 1000;
const uint16_t SHORT_ADC_TH = 5;
const uint8_t OPEN_SHORT_CONFIRM = 3;
// =====================================
// 2倍表示時の仮想文字幅
// =====================================
uint8_t charWidth2x(char c) {
if ((c >= '0' && c <= '9') || c == 'k' || c == 'M') {
return 16;
}
if (c == '.') {
return 8;
}
return 16;
}
uint8_t textWidth2x(const char *s) {
uint8_t w = 0;
for (uint8_t i = 0; s[i]; i++) {
w += charWidth2x(s[i]);
}
return w;
}
// =====================================
// ユーティリティ
// =====================================
void sortU16(uint16_t *arr, uint8_t n) {
for (uint8_t i = 0; i < n - 1; i++) {
for (uint8_t j = i + 1; j < n; j++) {
if (arr[j] < arr[i]) {
uint16_t t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
}
}
uint32_t absDiffU32(uint32_t a, uint32_t b) {
return (a > b) ? (a - b) : (b - a);
}
uint32_t pow10u8(uint8_t n) {
uint32_t p = 1;
while (n--) {
p *= 10UL;
}
return p;
}
// =====================================
// 未挿入メッセージ
// =====================================
void showInsertMessage() {
oled.clear();
oled.setFont(System5x7);
oled.set1X();
const char* line1 = "Insert";
const char* line2 = "Resistor";
int16_t x1 = (128 - (int16_t)strlen(line1) * 6) / 2;
int16_t x2 = (128 - (int16_t)strlen(line2) * 6) / 2;
if (x1 < 0) x1 = 0;
if (x2 < 0) x2 = 0;
oled.setCursor(x1, 0);
oled.print(line1);
oled.setCursor(x2, 2);
oled.print(line2);
}
// =====================================
// ADC複数回読み + 中央付近平均
// =====================================
uint16_t readAdcFiltered() {
uint16_t samples[ADC_SAMPLES];
analogRead(ADC_PIN);
delayMicroseconds(ADC_SAMPLE_DELAY_US);
for (uint8_t i = 0; i < ADC_SAMPLES; i++) {
samples[i] = analogRead(ADC_PIN);
delayMicroseconds(ADC_SAMPLE_DELAY_US);
}
sortU16(samples, ADC_SAMPLES);
uint32_t sum = 0;
uint8_t count = 0;
for (uint8_t i = ADC_AVG_START; i <= ADC_AVG_END; i++) {
sum += samples[i];
count++;
}
return (uint16_t)(sum / count);
}
// =====================================
// ADC -> 抵抗値
// Rx = Rref * a / (1023 - a)
// =====================================
uint32_t adcToResistance(uint16_t a) {
if (a >= 1023) return 0xFFFFFFFFUL;
if (a == 0) return 0;
return (uint32_t)RREF * a / (1023UL - a);
}
// =====================================
// 表示更新用しきい値
// =====================================
uint32_t getUpdateThreshold(uint32_t prevRx) {
if (prevRx < 1000UL) {
return 10;
} else if (prevRx < 10000UL) {
uint32_t t = prevRx / 50UL;
if (t < 20) t = 20;
return t;
} else if (prevRx < 100000UL) {
uint32_t t = prevRx * 3UL / 100UL;
if (t < 500) t = 500;
return t;
} else if (prevRx < 1000000UL) {
uint32_t t = prevRx * 5UL / 100UL;
if (t < 5000) t = 5000;
return t;
} else {
uint32_t t = prevRx * 8UL / 100UL;
if (t < 50000) t = 50000;
return t;
}
}
// =====================================
// E12系列へ吸着
// 1.0, 1.2, 1.5, 1.8, 2.2, 2.7,
// 3.3, 3.9, 4.7, 5.6, 6.8, 8.2, 10.0
// =====================================
uint32_t snapToE12(uint32_t rx) {
if (rx == 0) return 0;
static const uint16_t e12x10[13] = {
10, 12, 15, 18, 22, 27, 33, 39, 47, 56, 68, 82, 100
};
uint32_t norm = rx;
uint8_t exp10 = 0;
while (norm >= 100UL) {
norm /= 10UL;
exp10++;
}
uint32_t best = e12x10[0];
uint32_t bestDiff = absDiffU32(norm, e12x10[0]);
for (uint8_t i = 1; i < 13; i++) {
uint32_t diff = absDiffU32(norm, e12x10[i]);
if (diff < bestDiff) {
bestDiff = diff;
best = e12x10[i];
}
}
return best * pow10u8(exp10);
}
// =====================================
// 表示文字列作成
//
// unit:
// 0 = Ω
// 1 = kΩ(小数1桁)
// 2 = kΩ(整数)
// 3 = MΩ(小数1桁)
// =====================================
void buildDisplayText(uint32_t rx, uint8_t &unit, uint32_t &val, char *buf, uint8_t bufSize) {
buf[0] = '\0';
unit = 0;
val = 0;
uint32_t dispRx;
// 1kΩ未満ではE12丸めをしない
if (rx < 100UL) {
dispRx = rx;
} else {
dispRx = snapToE12(rx);
}
if (dispRx < 1000UL) {
unit = 0;
val = dispRx;
ultoa(val, buf, 10);
return;
}
if (dispRx < 10000UL) {
unit = 1;
val = dispRx / 100UL; // 0.1kΩ単位
ultoa(val / 10UL, buf, 10);
uint8_t idx = strlen(buf);
if (idx < bufSize - 3) {
buf[idx++] = '.';
buf[idx++] = '0' + (val % 10UL);
buf[idx++] = 'k';
buf[idx] = '\0';
}
return;
}
if (dispRx < 1000000UL) {
unit = 2;
val = dispRx / 1000UL; // 整数kΩ
ultoa(val, buf, 10);
uint8_t idx = strlen(buf);
if (idx < bufSize - 1) {
buf[idx++] = 'k';
buf[idx] = '\0';
}
return;
}
unit = 3;
val = dispRx / 100000UL; // 0.1MΩ単位
ultoa(val / 10UL, buf, 10);
uint8_t idx = strlen(buf);
if (idx < bufSize - 3) {
buf[idx++] = '.';
buf[idx++] = '0' + (val % 10UL);
buf[idx++] = 'M';
buf[idx] = '\0';
}
}
// =====================================
// 表示描画
// =====================================
void drawResistanceText(const char *buf) {
const uint8_t OLED_W = 128;
const uint8_t OMEGA_W2 = 16;
uint8_t w_num = textWidth2x(buf);
uint8_t total_w = w_num + OMEGA_W2;
int16_t x = (OLED_W - total_w) / 2;
x += CENTER_OFFSET;
if (x < 0) x = 0;
oled.setFont(Arial14);
oled.set2X();
oled.clear();
oled.setCursor(x, 0);
oled.print(buf);
int16_t xOmega = x + w_num;
oled.setCursor(xOmega, 0);
oled.print((char)0xCE);
oled.print((char)0xA9);
}
// =====================================
// setup
// =====================================
void setup() {
delay(200);
Wire.begin();
delay(50);
oled.begin(&Adafruit128x32, OLED_ADDR);
oled.setFont(Arial14);
oled.set2X();
oled.clear();
showInsertMessage();
}
// =====================================
// loop
// =====================================
void loop() {
static bool prevOpenScreen = true;
static uint8_t openCount = 0;
static uint8_t shortCount = 0;
static uint32_t filteredRx = 0;
static uint32_t prevRxShown = 0;
static uint32_t prevVal = 0xFFFFFFFFUL;
static uint8_t prevUnit = 0xFF;
uint16_t a = readAdcFiltered();
bool isOpenNow = (a >= OPEN_ADC_TH);
bool isShortNow = (a <= SHORT_ADC_TH);
if (isOpenNow) {
if (openCount < 255) openCount++;
} else {
openCount = 0;
}
if (isShortNow) {
if (shortCount < 255) shortCount++;
} else {
shortCount = 0;
}
if (isOpenNow || isShortNow) {
if ((openCount >= OPEN_SHORT_CONFIRM) || (shortCount >= OPEN_SHORT_CONFIRM)) {
if (!prevOpenScreen) {
showInsertMessage();
prevOpenScreen = true;
}
filteredRx = 0;
prevRxShown = 0;
prevVal = 0xFFFFFFFFUL;
prevUnit = 0xFF;
}
delay(LOOP_DELAY_MS);
return;
}
if (prevOpenScreen) {
oled.clear();
prevOpenScreen = false;
}
uint32_t rxRaw = adcToResistance(a);
if (filteredRx == 0) {
filteredRx = rxRaw;
} else {
filteredRx =
(
filteredRx * (RX_FILTER_DEN - RX_FILTER_NUM) +
rxRaw * RX_FILTER_NUM
) / RX_FILTER_DEN;
}
uint32_t rx = filteredRx;
if (prevRxShown != 0) {
uint32_t diff = absDiffU32(rx, prevRxShown);
uint32_t threshold = getUpdateThreshold(prevRxShown);
if (diff < threshold) {
delay(LOOP_DELAY_MS);
return;
}
}
char buf[16];
uint8_t unit;
uint32_t val;
buildDisplayText(rx, unit, val, buf, sizeof(buf));
if (val == prevVal && unit == prevUnit) {
delay(LOOP_DELAY_MS);
return;
}
prevVal = val;
prevUnit = unit;
prevRxShown = rx;
drawResistanceText(buf);
delay(LOOP_DELAY_MS);
}
小型のATTINY402も試しましたが、容量が足りなかったため、ATTINY1604を使用しました。
回路解説

分圧回路で電圧を読み取り、そこから抵抗値を計算しています。
基準抵抗として使用している10kΩと、測定する抵抗が同じ10kΩだった場合、マイコンは約1.5Vを計測します。
測定する抵抗値が基準抵抗より大きくなると、マイコンが計測する電圧も高くなります。
逆に、測定する抵抗値が基準抵抗より小さくなると、マイコンが計測する電圧は低くなります。
組み込み
基板とケースは、JLCPCBに注文しました。
特に難しいところはありませんが、抵抗のリードを差し込むブレッドボード部分だけ、少し工夫しています。
縦方向の列はどこに差しても導通するようにしたかったため、ブレッドボードを分解し、内部を銅箔テープで接続しました。

掛かった費用
- 基板代:100円
- ケース代:500円
- OLEDディスプレイ:500円
- IC(ATTINY1604):100円
- CR2032電池&ケース:200円
- スライドスイッチ:30円
- ブレッドボード:200円
- その他(抵抗/コンデンサ/コネクタ等):100円
総費用:約1,730円



