Arduino jako měřič impedance

Odpovědět
Uživatelský avatar
JPLABS
Příspěvky: 68
Registrován: 28 pro 2025, 16:52
Bydliště: Praha
Kontaktovat uživatele:

Arduino jako měřič impedance

Příspěvek od JPLABS » 24 kvě 2026, 17:48

Napadl mne jeden smysluplný projekt pro borce Arduinisty. Je to projekt, kde je málo vedlejšího hardware a hlavně to stojí a padá se software. Možno použít Arduino. Kromě Arduina potřebujete ještě:
3 kusy odporů
3 kusy kondenzátorů
3 kusy signálových diod
1 kus displej, doporučuju barevný grafický
1 kus kovové krabičky
2 kusy konektorů pro připojení napájení a pro připojení testované součástky.

To je všechno. Když vhodně a šikovně zapojíte ty tři odpory a tři kondenzátory a tři diody k Božskému Arduinu a sestavíte software pro Božské Arduino, můžete měřit impedanci obvodů a součástek a zobrazovat závislost impedance do grafů.
Kterýpak borec Arduinista toto dokáže vyřešit?
S časem se vše zhoršuje (zákon prof. Parkinsona)
Obrázek

Uživatelský avatar
Caster
Příspěvky: 521
Registrován: 11 zář 2019, 09:02

Re: Arduino jako měřič impedance

Příspěvek od Caster » 25 kvě 2026, 03:05

Schéma zapojení:

Obrázek

Arduino program:

Kód: Vybrat vše

/*
  Arduino Impedancni Analyzator v1.0.0
  Deska: Arduino UNO R4 Minima / UNO R4 WiFi (nutny skutecny DAC na A0)
  Displej: ILI9341 320x240 SPI, knihovny Adafruit_GFX + Adafruit_ILI9341

  Zapojeni, ktere tento program ocekava:
    A0/DAC OUT ---- VIN ---- RREF 1k ---- VOUT ---- Zx ---- GND
                          |             |
                         A1            A2

  TFT ILI9341:
    CS  -> D10
    DC  -> D9
    RST -> D8
    MOSI/SCK -> hardwarove SPI piny desky

  DULEZITE:
  - Vstupy A1 a A2 meri uzly VIN a VOUT. V merici ceste nesmi byt RC filtry,
    ktere by menily amplitudu nebo fazi signalu.
  - DAC vytvari kladne posunuty sinusovy signal; neni potreba seriovy vazebni
    kondenzator C1. Pokud ho pouzijete, je nutne doplnit spravne DC predpeti.
  - S jedinym referencnim odporem 1 kOhm je rozumny prakticky rozsah priblizne
    100 Ohm az 10 kOhm. Pro Ohmy az MOhmy je nutne prepinani RREF.

  Princip:
    I = (VIN - VOUT) / RREF
    Zx = VOUT / I = RREF * VOUT / (VIN - VOUT)

  Program snima komplexni zakladni harmonickou pomoci synchronni demodulace,
  takze zobrazuje modul |Z| i fazi impedance.
*/

#include <Arduino.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <math.h>

#define PROGRAM_VERSION "v1.0.0"

// ------------------------------ Piny ---------------------------------------
static constexpr uint8_t PIN_DAC  = A0;
static constexpr uint8_t PIN_VIN  = A1;
static constexpr uint8_t PIN_VOUT = A2;

static constexpr uint8_t TFT_CS  = 10;
static constexpr uint8_t TFT_DC  = 9;
static constexpr uint8_t TFT_RST = 8;

Adafruit_ILI9341 tft(TFT_CS, TFT_DC, TFT_RST);

// --------------------------- Merici parametry ------------------------------
static constexpr float RREF_OHM = 1000.0f;
static constexpr uint16_t DAC_MID = 2048;       // 12bit DAC; stred signalu
static constexpr uint16_t DAC_AMPLITUDE = 1000; // bezpecne pod maximem 4095
static constexpr uint8_t SETTLE_CYCLES = 3;
static constexpr uint8_t MEASURE_CYCLES = 6;
static constexpr uint8_t MAX_POINTS_PER_CYCLE = 48;

static const float TEST_FREQUENCIES_HZ[] = {
  100.0f, 160.0f, 250.0f, 400.0f, 630.0f,
  1000.0f, 1600.0f, 2500.0f, 4000.0f
};
static constexpr uint8_t FREQ_COUNT = sizeof(TEST_FREQUENCIES_HZ) / sizeof(TEST_FREQUENCIES_HZ[0]);

// ------------------------------ Graf ---------------------------------------
static constexpr int16_t GRAPH_X = 42;
static constexpr int16_t GRAPH_Y = 49;
static constexpr int16_t GRAPH_W = 267;
static constexpr int16_t GRAPH_H = 130;
static constexpr float GRAPH_Z_MIN = 10.0f;
static constexpr float GRAPH_Z_MAX = 100000.0f;

struct ComplexValue {
  float re;
  float im;
};

struct Measurement {
  float frequencyHz;
  float magnitudeOhm;
  float phaseDeg;
  float vinMagnitude;
  float voutMagnitude;
  bool valid;
};

Measurement measurements[FREQ_COUNT];
bool continuousMode = true;
uint32_t lastSweepMs = 0;

// ------------------------- Komplexni matematika ----------------------------
ComplexValue complexSubtract(const ComplexValue &a, const ComplexValue &b) {
  return {a.re - b.re, a.im - b.im};
}

float complexMagnitude(const ComplexValue &a) {
  return sqrtf(a.re * a.re + a.im * a.im);
}

ComplexValue complexDivide(const ComplexValue &a, const ComplexValue &b) {
  const float d = b.re * b.re + b.im * b.im;
  if (d < 1.0e-12f) {
    return {NAN, NAN};
  }
  return {(a.re * b.re + a.im * b.im) / d,
          (a.im * b.re - a.re * b.im) / d};
}

// ----------------------------- Pomocne funkce ------------------------------
uint8_t pointsPerCycleForFrequency(float frequencyHz) {
  if (frequencyHz <= 1000.0f) return 48;
  if (frequencyHz <= 2500.0f) return 32;
  return 20;
}

uint16_t dacCodeForPhase(float phaseRad) {
  const float value = static_cast<float>(DAC_MID) + static_cast<float>(DAC_AMPLITUDE) * sinf(phaseRad);
  if (value < 0.0f) return 0;
  if (value > 4095.0f) return 4095;
  return static_cast<uint16_t>(value + 0.5f);
}

void waitUntilMicros(uint32_t targetUs) {
  while (static_cast<int32_t>(micros() - targetUs) < 0) {
    // Aktivni cekani zamerne: udrzuje pravidelnost buzeni a vzorkovani.
  }
}

void setDacMidpoint() {
  analogWrite(PIN_DAC, DAC_MID);
}

// ------------------------------- Mereni ------------------------------------
Measurement measureAtFrequency(float frequencyHz) {
  Measurement result{};
  result.frequencyHz = frequencyHz;
  result.valid = false;

  const uint8_t points = pointsPerCycleForFrequency(frequencyHz);
  const uint16_t samples = static_cast<uint16_t>(points) * MEASURE_CYCLES;
  const float stepPeriodUsF = 1000000.0f / (frequencyHz * static_cast<float>(points));
  const uint32_t stepPeriodUs = static_cast<uint32_t>(stepPeriodUsF + 0.5f);

  // Frekvence, kterou ve skutecnosti nastavime celočíselnym casovanim.
  const float actualFrequencyHz = 1000000.0f / (static_cast<float>(stepPeriodUs) * static_cast<float>(points));
  result.frequencyHz = actualFrequencyHz;

  uint32_t nextUs = micros() + 200;

  // Ustaleni obvodu pred vlastnim snimanim.
  for (uint16_t n = 0; n < static_cast<uint16_t>(points) * SETTLE_CYCLES; ++n) {
    const uint8_t k = n % points;
    const float phase = TWO_PI * static_cast<float>(k) / static_cast<float>(points);
    analogWrite(PIN_DAC, dacCodeForPhase(phase));
    nextUs += stepPeriodUs;
    waitUntilMicros(nextUs);
  }

  ComplexValue vin{0.0f, 0.0f};
  ComplexValue vout{0.0f, 0.0f};

  for (uint16_t n = 0; n < samples; ++n) {
    const uint8_t k = n % points;
    const float phase = TWO_PI * static_cast<float>(k) / static_cast<float>(points);
    const float c = cosf(phase);
    const float s = sinf(phase);

    analogWrite(PIN_DAC, dacCodeForPhase(phase));

    // Vzorek se odebere priblizne uprostred nastaveneho DAC kroku.
    waitUntilMicros(nextUs + stepPeriodUs / 2U);
    const float rawVin = static_cast<float>(analogRead(PIN_VIN));
    const float rawVout = static_cast<float>(analogRead(PIN_VOUT));

    vin.re += rawVin * c;
    vin.im -= rawVin * s;
    vout.re += rawVout * c;
    vout.im -= rawVout * s;

    nextUs += stepPeriodUs;
    waitUntilMicros(nextUs);
  }

  setDacMidpoint();

  const float scale = 2.0f / static_cast<float>(samples);
  vin.re *= scale;
  vin.im *= scale;
  vout.re *= scale;
  vout.im *= scale;

  const ComplexValue delta = complexSubtract(vin, vout);
  const float deltaMagnitude = complexMagnitude(delta);
  result.vinMagnitude = complexMagnitude(vin);
  result.voutMagnitude = complexMagnitude(vout);

  // Pri velmi malem napeti na RREF nelze spolehlive urcit proud.
  if (result.vinMagnitude < 2.0f || deltaMagnitude < 1.5f) {
    result.magnitudeOhm = NAN;
    result.phaseDeg = NAN;
    return result;
  }

  ComplexValue z = complexDivide(vout, delta);
  z.re *= RREF_OHM;
  z.im *= RREF_OHM;

  result.magnitudeOhm = complexMagnitude(z);
  result.phaseDeg = atan2f(z.im, z.re) * 180.0f / PI;
  result.valid = isfinite(result.magnitudeOhm) && isfinite(result.phaseDeg) && result.magnitudeOhm > 0.0f;
  return result;
}

// ------------------------------- Displej -----------------------------------
void drawHeader() {
  tft.fillScreen(ILI9341_BLACK);
  tft.setTextColor(ILI9341_CYAN);
  tft.setTextSize(2);
  tft.setCursor(6, 6);
  tft.print(F("IMPEDANCE |Z|"));

  tft.setTextColor(ILI9341_WHITE);
  tft.setTextSize(1);
  tft.setCursor(226, 10);
  tft.print(PROGRAM_VERSION);

  tft.setCursor(6, 28);
  tft.print(F("UNO R4 DAC A0 | VIN A1 | VOUT A2 | Rref=1k"));
}

int16_t graphYForMagnitude(float magnitudeOhm) {
  if (!isfinite(magnitudeOhm) || magnitudeOhm <= 0.0f) return GRAPH_Y + GRAPH_H;
  float z = magnitudeOhm;
  if (z < GRAPH_Z_MIN) z = GRAPH_Z_MIN;
  if (z > GRAPH_Z_MAX) z = GRAPH_Z_MAX;
  const float fraction = (log10f(z) - log10f(GRAPH_Z_MIN)) /
                         (log10f(GRAPH_Z_MAX) - log10f(GRAPH_Z_MIN));
  return GRAPH_Y + GRAPH_H - static_cast<int16_t>(fraction * GRAPH_H + 0.5f);
}

int16_t graphXForIndex(uint8_t index) {
  if (FREQ_COUNT <= 1) return GRAPH_X;
  return GRAPH_X + static_cast<int16_t>((static_cast<long>(index) * GRAPH_W) / (FREQ_COUNT - 1));
}

void drawGraphBackground() {
  tft.fillRect(0, 42, 320, 145, ILI9341_BLACK);
  tft.drawRect(GRAPH_X, GRAPH_Y, GRAPH_W, GRAPH_H, ILI9341_DARKGREY);

  const float labels[] = {10.0f, 100.0f, 1000.0f, 10000.0f, 100000.0f};
  const char *labelText[] = {"10", "100", "1k", "10k", "100k"};
  tft.setTextSize(1);
  for (uint8_t i = 0; i < 5; ++i) {
    const int16_t y = graphYForMagnitude(labels[i]);
    tft.drawFastHLine(GRAPH_X, y, GRAPH_W, ILI9341_DARKGREY);
    tft.setTextColor(ILI9341_WHITE);
    tft.setCursor(4, y - 3);
    tft.print(labelText[i]);
  }

  for (uint8_t i = 0; i < FREQ_COUNT; ++i) {
    const int16_t x = graphXForIndex(i);
    tft.drawFastVLine(x, GRAPH_Y, GRAPH_H, ILI9341_DARKGREY);
  }

  tft.setTextColor(ILI9341_WHITE);
  tft.setCursor(GRAPH_X, GRAPH_Y + GRAPH_H + 4);
  tft.print(F("100Hz"));
  tft.setCursor(GRAPH_X + 102, GRAPH_Y + GRAPH_H + 4);
  tft.print(F("1k"));
  tft.setCursor(GRAPH_X + GRAPH_W - 30, GRAPH_Y + GRAPH_H + 4);
  tft.print(F("4k"));
}

void displayLatestValues(const Measurement &m) {
  tft.fillRect(0, 195, 320, 45, ILI9341_BLACK);
  tft.setTextSize(1);
  tft.setTextColor(ILI9341_YELLOW);
  tft.setCursor(6, 198);
  tft.print(F("Posledni: "));
  tft.print(m.frequencyHz, 0);
  tft.print(F(" Hz"));

  tft.setCursor(6, 212);
  if (m.valid) {
    tft.setTextColor(ILI9341_GREEN);
    tft.print(F("|Z|="));
    if (m.magnitudeOhm >= 1000.0f) {
      tft.print(m.magnitudeOhm / 1000.0f, 2);
      tft.print(F(" kOhm"));
    } else {
      tft.print(m.magnitudeOhm, 1);
      tft.print(F(" Ohm"));
    }
    tft.setTextColor(ILI9341_MAGENTA);
    tft.setCursor(172, 212);
    tft.print(F("faze="));
    tft.print(m.phaseDeg, 1);
    tft.print(F(" deg"));
  } else {
    tft.setTextColor(ILI9341_RED);
    tft.print(F("MIMO ROZSAH / bez proudu"));
  }

  tft.setTextColor(ILI9341_WHITE);
  tft.setCursor(6, 227);
  tft.print(F("Serial: s=scan, c=continuous ON/OFF"));
}

void plotMeasurements() {
  drawGraphBackground();
  bool havePrevious = false;
  int16_t previousX = 0;
  int16_t previousY = 0;

  for (uint8_t i = 0; i < FREQ_COUNT; ++i) {
    if (!measurements[i].valid) {
      havePrevious = false;
      continue;
    }
    const int16_t x = graphXForIndex(i);
    const int16_t y = graphYForMagnitude(measurements[i].magnitudeOhm);
    if (havePrevious) {
      tft.drawLine(previousX, previousY, x, y, ILI9341_GREEN);
    }
    tft.fillCircle(x, y, 2, ILI9341_YELLOW);
    previousX = x;
    previousY = y;
    havePrevious = true;
  }
}

// ------------------------------- Sweep -------------------------------------
void runSweep() {
  tft.fillRect(0, 186, 320, 9, ILI9341_BLACK);
  tft.setTextSize(1);
  tft.setTextColor(ILI9341_CYAN);
  tft.setCursor(6, 187);
  tft.print(F("Merim..."));

  Serial.println();
  Serial.println(F("frequency_Hz;abs_Z_Ohm;phase_deg;Vin_DFT;Vout_DFT;status"));

  for (uint8_t i = 0; i < FREQ_COUNT; ++i) {
    measurements[i] = measureAtFrequency(TEST_FREQUENCIES_HZ[i]);
    const Measurement &m = measurements[i];

    Serial.print(m.frequencyHz, 2);
    Serial.print(';');
    if (m.valid) {
      Serial.print(m.magnitudeOhm, 3);
      Serial.print(';');
      Serial.print(m.phaseDeg, 3);
      Serial.print(';');
      Serial.print(m.vinMagnitude, 3);
      Serial.print(';');
      Serial.print(m.voutMagnitude, 3);
      Serial.println(F(";OK"));
    } else {
      Serial.print(F("NaN;NaN;"));
      Serial.print(m.vinMagnitude, 3);
      Serial.print(';');
      Serial.print(m.voutMagnitude, 3);
      Serial.println(F(";OUT_OF_RANGE"));
    }
  }

  plotMeasurements();
  displayLatestValues(measurements[FREQ_COUNT - 1]);

  tft.fillRect(0, 186, 320, 9, ILI9341_BLACK);
  tft.setTextColor(ILI9341_CYAN);
  tft.setCursor(6, 187);
  tft.print(continuousMode ? F("AUTO SWEEP") : F("MANUAL MODE"));

  lastSweepMs = millis();
}

// ------------------------------ Arduino API --------------------------------
void setup() {
  Serial.begin(115200);
  delay(300);

  analogWriteResolution(12);
  analogReadResolution(12);
  setDacMidpoint();

  tft.begin();
  tft.setRotation(1);
  drawHeader();
  drawGraphBackground();

  tft.setTextSize(1);
  tft.setTextColor(ILI9341_WHITE);
  tft.setCursor(6, 198);
  tft.print(F("Start mereni..."));

  Serial.println(F("Arduino Impedancni Analyzator " PROGRAM_VERSION));
  Serial.println(F("UNO R4: DAC=A0, VIN=A1, VOUT=A2, RREF=1000 Ohm"));
  Serial.println(F("Poznamka: s jednim RREF=1k je prakticky rozsah zhruba 100 Ohm az 10 kOhm."));
  Serial.println(F("Prikazy: s = jednorazovy sweep, c = prepnout automaticke mereni."));

  runSweep();
}

void loop() {
  if (Serial.available() > 0) {
    const char command = static_cast<char>(Serial.read());
    if (command == 's' || command == 'S') {
      runSweep();
    } else if (command == 'c' || command == 'C') {
      continuousMode = !continuousMode;
      Serial.print(F("Continuous mode: "));
      Serial.println(continuousMode ? F("ON") : F("OFF"));
      displayLatestValues(measurements[FREQ_COUNT - 1]);
    }
  }

  if (continuousMode && millis() - lastSweepMs >= 1500UL) {
    runSweep();
  }
}

Uživatelský avatar
JPLABS
Příspěvky: 68
Registrován: 28 pro 2025, 16:52
Bydliště: Praha
Kontaktovat uživatele:

Re: Arduino jako měřič impedance

Příspěvek od JPLABS » 29 kvě 2026, 00:29

koukal jsem na to jak jelen. Chvíli trvalo, než jsem se vzpamatoval.
Proboha, co to je zase za ptákovinu? Kdo je autorem?
Impedance má reálnou složku a imaginární složku. To co se v tom schematu měří nemá s impedancí nic společnýho. Z té rovnice se úplně vytratila frekvence. Krom toho, pro měření impedance pomocí voltmetrů jsou potřeba 3 střídavé (AC) voltmetry. Zde jsou jen dva. A to se ještě měří jakési napětí před a za referenčním odporem, který není nijak zapojen k měřené součástce. Jaký asi bude rozíl mezi napětím před a za referenčním odporem?
Co si mám představit pod tím "referenčním" odporem , které jsou zřejmě dva :lol: každý má 100 kOhm. Tedy nebudou úplně stejné. Jakou mají mít indukčnost? Ty odpory mají převážně reálnou složku, imaginární složka bude dost nízká. Jak můžou fungovat jako reference, když nejsou nijak zapojený k testovaný součástce? Ty diody tam akorát odříznou zápornou půlvlnu.
A co ten kondík 100nF paralelně k té testované součástce? Jaký má význam?
Jak se z toho měření vydedukuje imaginární a reálná složka?
Za další, zásadně se měří se vstupním a výstupním odporem 50 Ohm a pomocí čistého sinusového signálu, nikoliv schodovitého průběhu získaného z DDS nebo DA převodníku.
A nakonec, graf ise vykresluje do tzv. Shmithova diagramu https://en.wikipedia.org/wiki/Smith_chart.
S časem se vše zhoršuje (zákon prof. Parkinsona)
Obrázek

Uživatelský avatar
JPLABS
Příspěvky: 68
Registrován: 28 pro 2025, 16:52
Bydliště: Praha
Kontaktovat uživatele:

Re: Arduino jako měřič impedance

Příspěvek od JPLABS » 30 kvě 2026, 13:32

přemýšlel jsem o té "ptákovině" co sem dal Caster. To je určitě odněkud stažený z internetu. Impedanci to neměří, spíš nějakou závislost reálnýho odporu na kmitočtu. Uniká mi ale smysl. Smysl by to ještě dávalo pro vysoký kmitočty v řádech stovek MHz, kde odpor součástky, typicky SMD odporu, závisí na keramické hmotě, z které je odporová součástka vyrobena (poznámka: Yageo jsou nejlepší).

Ale napadlo mne, jak by jste mohli začít s Božským Arduinem a něco užitečnýho se přitom naučit :D .
Takže domácí úloha:
vezměte Božské Arduino, připojte na něj grafický barevný displej, ten největší co do rozlišení i velikosti seženete. Žádný malý "prdítko" 100x100 pixlů.
Potom naprogramujte do Arduina program, který vykreslí prázdný Smidtův diagram:
https://upload.wikimedia.org/wikipedia/ ... ations.png
Až toto budete mít, teprve potom můžete sestavit přípravek pro měření impedance a snímání dat z toho hardwarovýho přípravku pomocí Arduina a následovně pomocí Božského Arduina vypočítat reálnou a imaginární složku impedance, zakreslit ji do Smidtova diagramu a vypočíst další hodnoty pro měřenou součástku.

Vylepšení pro "borce": můžete také sehnat nějaký starý plotter, buď po RS232C nebo XY plotter (to je analogový) a připojit ho k Božskému Arduinu a ten Smidtův diagram vykreslit z Arduina na plotteru. Toto když pak předvedete panu učiteli ve škole, tak to s ním sekne a máte maturitu s vyznamenáním v kapse. :lol:
S časem se vše zhoršuje (zákon prof. Parkinsona)
Obrázek

Uživatelský avatar
Caster
Příspěvky: 521
Registrován: 11 zář 2019, 09:02

Re: Arduino jako měřič impedance

Příspěvek od Caster » 31 kvě 2026, 20:13

@JPLABS

VIN i VOUT se měří jako komplexní fázory pomocí synchronní detekce (lock-in / jednobinová DFT). To jsou přesně tyhle řádky:
vin.re += rawVin * cosf(phVin);
vin.im -= rawVin * sinf(phVin);
vout.re += rawVout * cosf(phVout);
vout.im -= rawVout * sinf(phVout);
Pak se dělá complexSubtract(vin, vout) a complexDivide(vout, delta) — tedy celý vztah Zx = Rref·VOUT/(VIN−VOUT) běží v komplexní rovině. Klíčové je, že (VIN − VOUT) není |VIN| − |VOUT|; je to komplexní rozdíl, který nese amplitudu i fázi proudu rezistorem. Výsledek Z má proto reálnou i imaginární složku (z.re, z.im) a kód z nich počítá jak modul, tak fázi přes atan2(z.im, z.re). Reálná i imaginární složka tam tedy jsou — kondenzátor vyjde s fází blízko −90°, cívka +90°.

K té „zmizelé frekvenci": to je ve skutečnosti správně a záměrně. Z = U/I je definice impedance při dané budicí frekvenci — frekvence do toho vzorce explicitně nepatří. Srovnej s Ohmovým zákonem: i čistý rezistor má Z = R bez jakéhokoli ω, a pořád je to impedance. ω se objeví, až když napíšeš uzavřený vzorec konkrétní součástky (Z = 1/jωC). Jenže analyzátor žádný uzavřený vzorec nepočítá — on prostě budí na frekvenci f, změří komplexní U a I a vydělí je. Frekvenční závislost je schovaná v naměřených fázorech a v tom, že se měří ve sweepu.

Konkrétně: 1µF kondenzátor má při 100 Hz Z ≈ −j1592 Ω, při 1 kHz Z ≈ −j159 Ω. Tenhle obvod oba případy rozliší správně, protože naměřené fázory VOUT a (VIN−VOUT) jsou na těch dvou frekvencích jiné. A sweep 100 Hz – 4 kHz přesně tohle dělá — dává ti Z(f), tedy spektrum. To je definice impedančního analyzátoru.

Kde má tvá připomínka praktický základ: rámeček „PRINCIP MĚŘENÍ" ve schématu píše vzorec ve skalárním tvaru, bez jakékoli zmínky, že VIN a VOUT jsou komplexní fázory ze synchronní detekce. Kdo vidí jen ten obrázek a ne kód, snadno to přečte jako stejnosměrný dělič a dojde k přesně té námitce, kterou jsi napsal. Takže chyba není v měření, ale v prezentaci.

Opravené schéma:
Obrázek

Opravený kód:

Kód: Vybrat vše

/*
  Arduino Impedancni Analyzator v2.0.0  (opravena verze)
  Deska: Arduino UNO R4 Minima / UNO R4 WiFi (nutny skutecny DAC na A0)
  Displej: ILI9341 320x240 SPI, knihovny Adafruit_GFX + Adafruit_ILI9341

  Zapojeni, ktere tento program ocekava:
    A0/DAC OUT ---- VIN ---- RREF 1k ---- VOUT ---- Zx ---- GND
                     |                      |
              R2 1k -+-> A1          R3 1k -+-> A2
                     |  |                   |  |
                C1 100p D1 (->GND)     C2 100p D2 (->GND)

  ZMENY OPROTI PUVODNIMU NAVRHU:
   - C1, C2 zmenseny ze 100 nF na 100 pF. Puvodnich 100 nF se 100k tvorilo
     dolni propust se zlomem ~16 Hz a tlumilo cele merici pasmo 100 Hz - 4 kHz.
     S R = 1k a C = 100 pF je zlom ~1,6 MHz, tj. mimo merici pasmo. Kondenzatory
     tak slouzi jen jako RFI / anti-alias filtr a nemeni amplitudu ani fazi.
   - R2, R3 zmenseny ze 100k na 1k -> rychle ustaleni vstupu ADC, stale
     dostatecna seriova ochrana spolu s clampovacimi diodami D1/D2.
   - C3 (100 nF) NENI v merici ceste u uzlu VOUT, ale jako blokovaci kondenzator
     napajeni. U VOUT by zkratoval DUT (|Z|~400 Ohm pri 4 kHz).
   - Demodulace probiha podle SKUTECNEHO casu odberu kazdeho vzorku. Tim se
     automaticky odstrani fazovy skew mezi A1 a A2 (dve postupne konverze ADC)
     i casovy jitter. To je nejdulezitejsi oprava pro spravnou fazi a pro
     spravny modul u impedanci vyssich nez Rref.

  Princip:
    I  = (VIN - VOUT) / RREF
    Zx = VOUT / I = RREF * VOUT / (VIN - VOUT)

  Program snima komplexni zakladni harmonickou pomoci synchronni demodulace,
  takze zobrazuje modul |Z| i fazi impedance.
*/

#include <Arduino.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <math.h>

#define PROGRAM_VERSION "v2.0.0"

// ------------------------------ Piny ---------------------------------------
static constexpr uint8_t PIN_DAC  = A0;
static constexpr uint8_t PIN_VIN  = A1;
static constexpr uint8_t PIN_VOUT = A2;

static constexpr uint8_t TFT_CS  = 10;
static constexpr uint8_t TFT_DC  = 9;
static constexpr uint8_t TFT_RST = 8;
// SPI: SCK = D13, MOSI = D11 (hardwarove SPI piny UNO R4)

Adafruit_ILI9341 tft(TFT_CS, TFT_DC, TFT_RST);

// --------------------------- Merici parametry ------------------------------
static constexpr float RREF_OHM = 1000.0f;
static constexpr uint16_t DAC_MID = 2048;       // 12bit DAC; stred signalu
static constexpr uint16_t DAC_AMPLITUDE = 1000; // bezpecne pod maximem 4095
static constexpr uint8_t SETTLE_CYCLES = 3;
static constexpr uint8_t MEASURE_CYCLES = 6;
static constexpr uint8_t MAX_POINTS_PER_CYCLE = 48;

// Volitelna softwarova korekce zbytkove faze (v radianech). Zjisti se tak, ze
// se zmeri znamy CISTY rezistor (napr. 1k) a hodnota se nastavi tak, aby jeho
// namerena faze byla ~0 deg. Ponech 0.0f, pokud korekci nepouzivas.
static constexpr float PHASE_TRIM_RAD = 0.0f;

static const float TEST_FREQUENCIES_HZ[] = {
  100.0f, 160.0f, 250.0f, 400.0f, 630.0f,
  1000.0f, 1600.0f, 2500.0f, 4000.0f
};
static constexpr uint8_t FREQ_COUNT = sizeof(TEST_FREQUENCIES_HZ) / sizeof(TEST_FREQUENCIES_HZ[0]);

// ------------------------------ Graf ---------------------------------------
static constexpr int16_t GRAPH_X = 42;
static constexpr int16_t GRAPH_Y = 49;
static constexpr int16_t GRAPH_W = 267;
static constexpr int16_t GRAPH_H = 130;
static constexpr float GRAPH_Z_MIN = 10.0f;
static constexpr float GRAPH_Z_MAX = 100000.0f;

struct ComplexValue {
  float re;
  float im;
};

struct Measurement {
  float frequencyHz;
  float magnitudeOhm;
  float phaseDeg;
  float vinMagnitude;
  float voutMagnitude;
  bool valid;
};

Measurement measurements[FREQ_COUNT];
bool continuousMode = true;
uint32_t lastSweepMs = 0;

// ------------------------- Komplexni matematika ----------------------------
ComplexValue complexSubtract(const ComplexValue &a, const ComplexValue &b) {
  return {a.re - b.re, a.im - b.im};
}

float complexMagnitude(const ComplexValue &a) {
  return sqrtf(a.re * a.re + a.im * a.im);
}

ComplexValue complexDivide(const ComplexValue &a, const ComplexValue &b) {
  const float d = b.re * b.re + b.im * b.im;
  if (d < 1.0e-12f) {
    return {NAN, NAN};
  }
  return {(a.re * b.re + a.im * b.im) / d,
          (a.im * b.re - a.re * b.im) / d};
}

// Otoceni komplexniho cisla o uhel (pro softwarovy fazovy trim).
ComplexValue complexRotate(const ComplexValue &a, float angleRad) {
  const float c = cosf(angleRad);
  const float s = sinf(angleRad);
  return {a.re * c - a.im * s, a.re * s + a.im * c};
}

// ----------------------------- Pomocne funkce ------------------------------
uint8_t pointsPerCycleForFrequency(float frequencyHz) {
  if (frequencyHz <= 1000.0f) return 48;
  if (frequencyHz <= 2500.0f) return 32;
  return 20;
}

uint16_t dacCodeForPhase(float phaseRad) {
  const float value = static_cast<float>(DAC_MID) + static_cast<float>(DAC_AMPLITUDE) * sinf(phaseRad);
  if (value < 0.0f) return 0;
  if (value > 4095.0f) return 4095;
  return static_cast<uint16_t>(value + 0.5f);
}

void waitUntilMicros(uint32_t targetUs) {
  while (static_cast<int32_t>(micros() - targetUs) < 0) {
    // Aktivni cekani zamerne: udrzuje pravidelnost buzeni a vzorkovani.
  }
}

void setDacMidpoint() {
  analogWrite(PIN_DAC, DAC_MID);
}

// ------------------------------- Mereni ------------------------------------
Measurement measureAtFrequency(float frequencyHz) {
  Measurement result{};
  result.frequencyHz = frequencyHz;
  result.valid = false;

  const uint8_t points = pointsPerCycleForFrequency(frequencyHz);
  const uint16_t samples = static_cast<uint16_t>(points) * MEASURE_CYCLES;
  const float stepPeriodUsF = 1000000.0f / (frequencyHz * static_cast<float>(points));
  const uint32_t stepPeriodUs = static_cast<uint32_t>(stepPeriodUsF + 0.5f);

  // Frekvence, kterou ve skutecnosti nastavime celociselnym casovanim.
  const float actualFrequencyHz = 1000000.0f / (static_cast<float>(stepPeriodUs) * static_cast<float>(points));
  result.frequencyHz = actualFrequencyHz;

  // Uhlova frekvence v radianech na mikrosekundu (pro demodulaci podle casu).
  const float omega = TWO_PI * actualFrequencyHz * 1.0e-6f;

  uint32_t nextUs = micros() + 200;

  // Ustaleni obvodu pred vlastnim snimanim.
  for (uint16_t n = 0; n < static_cast<uint16_t>(points) * SETTLE_CYCLES; ++n) {
    const uint8_t k = n % points;
    const float phase = TWO_PI * static_cast<float>(k) / static_cast<float>(points);
    analogWrite(PIN_DAC, dacCodeForPhase(phase));
    nextUs += stepPeriodUs;
    waitUntilMicros(nextUs);
  }

  ComplexValue vin{0.0f, 0.0f};
  ComplexValue vout{0.0f, 0.0f};

  // Casova reference faze buzeni: zacatek mericiho okna (krok 0).
  const uint32_t windowStartUs = nextUs;

  for (uint16_t n = 0; n < samples; ++n) {
    const uint8_t k = n % points;
    const float drivePhase = TWO_PI * static_cast<float>(k) / static_cast<float>(points);

    // DAC drzi krokovany sinus podle drivePhase.
    analogWrite(PIN_DAC, dacCodeForPhase(drivePhase));

    // Vzorek se odebere priblizne uprostred nastaveneho DAC kroku.
    waitUntilMicros(nextUs + stepPeriodUs / 2U);

    // KLICOVA OPRAVA: kazdy kanal demoduluji podle SKUTECNEHO casu sveho
    // odberu (tVin, tVout). Dve konverze ADC bezi za sebou, takze A2 je
    // vzorkovan o jednu konverzi pozdeji nez A1. Pokud se oba kanaly
    // demoduluji stejnou nominalni fazi (puvodni kod), vznika differencni
    // fazova chyba ~2*pi*f*dt, ktera se NEvykrati a kazi fazi (a pres
    // komplexni deleni i modul). Pouzitim realneho casu odberu se skew
    // i pripadny jitter automaticky odstrani.
    const uint32_t tVin = micros();
    const float rawVin = static_cast<float>(analogRead(PIN_VIN));
    const uint32_t tVout = micros();
    const float rawVout = static_cast<float>(analogRead(PIN_VOUT));

    const float phVin  = omega * static_cast<float>(tVin  - windowStartUs);
    const float phVout = omega * static_cast<float>(tVout - windowStartUs);

    vin.re  += rawVin  * cosf(phVin);
    vin.im  -= rawVin  * sinf(phVin);
    vout.re += rawVout * cosf(phVout);
    vout.im -= rawVout * sinf(phVout);

    nextUs += stepPeriodUs;
    waitUntilMicros(nextUs);
  }

  setDacMidpoint();

  const float scale = 2.0f / static_cast<float>(samples);
  vin.re *= scale;
  vin.im *= scale;
  vout.re *= scale;
  vout.im *= scale;

  const ComplexValue delta = complexSubtract(vin, vout);
  const float deltaMagnitude = complexMagnitude(delta);
  result.vinMagnitude = complexMagnitude(vin);
  result.voutMagnitude = complexMagnitude(vout);

  // Pri velmi malem napeti na RREF nelze spolehlive urcit proud.
  if (result.vinMagnitude < 2.0f || deltaMagnitude < 1.5f) {
    result.magnitudeOhm = NAN;
    result.phaseDeg = NAN;
    return result;
  }

  ComplexValue z = complexDivide(vout, delta);
  z.re *= RREF_OHM;
  z.im *= RREF_OHM;

  // Volitelna softwarova korekce zbytkove faze (z kalibrace na znamem rezistoru).
  if (PHASE_TRIM_RAD != 0.0f) {
    z = complexRotate(z, PHASE_TRIM_RAD);
  }

  result.magnitudeOhm = complexMagnitude(z);
  result.phaseDeg = atan2f(z.im, z.re) * 180.0f / PI;
  result.valid = isfinite(result.magnitudeOhm) && isfinite(result.phaseDeg) && result.magnitudeOhm > 0.0f;
  return result;
}

// ------------------------------- Displej -----------------------------------
void drawHeader() {
  tft.fillScreen(ILI9341_BLACK);
  tft.setTextColor(ILI9341_CYAN);
  tft.setTextSize(2);
  tft.setCursor(6, 6);
  tft.print(F("IMPEDANCE |Z|"));

  tft.setTextColor(ILI9341_WHITE);
  tft.setTextSize(1);
  tft.setCursor(226, 10);
  tft.print(PROGRAM_VERSION);

  tft.setCursor(6, 28);
  tft.print(F("UNO R4 DAC A0 | VIN A1 | VOUT A2 | Rref=1k"));
}

int16_t graphYForMagnitude(float magnitudeOhm) {
  if (!isfinite(magnitudeOhm) || magnitudeOhm <= 0.0f) return GRAPH_Y + GRAPH_H;
  float z = magnitudeOhm;
  if (z < GRAPH_Z_MIN) z = GRAPH_Z_MIN;
  if (z > GRAPH_Z_MAX) z = GRAPH_Z_MAX;
  const float fraction = (log10f(z) - log10f(GRAPH_Z_MIN)) /
                         (log10f(GRAPH_Z_MAX) - log10f(GRAPH_Z_MIN));
  return GRAPH_Y + GRAPH_H - static_cast<int16_t>(fraction * GRAPH_H + 0.5f);
}

int16_t graphXForIndex(uint8_t index) {
  if (FREQ_COUNT <= 1) return GRAPH_X;
  return GRAPH_X + static_cast<int16_t>((static_cast<long>(index) * GRAPH_W) / (FREQ_COUNT - 1));
}

void drawGraphBackground() {
  tft.fillRect(0, 42, 320, 145, ILI9341_BLACK);
  tft.drawRect(GRAPH_X, GRAPH_Y, GRAPH_W, GRAPH_H, ILI9341_DARKGREY);

  const float labels[] = {10.0f, 100.0f, 1000.0f, 10000.0f, 100000.0f};
  const char *labelText[] = {"10", "100", "1k", "10k", "100k"};
  tft.setTextSize(1);
  for (uint8_t i = 0; i < 5; ++i) {
    const int16_t y = graphYForMagnitude(labels[i]);
    tft.drawFastHLine(GRAPH_X, y, GRAPH_W, ILI9341_DARKGREY);
    tft.setTextColor(ILI9341_WHITE);
    tft.setCursor(4, y - 3);
    tft.print(labelText[i]);
  }

  for (uint8_t i = 0; i < FREQ_COUNT; ++i) {
    const int16_t x = graphXForIndex(i);
    tft.drawFastVLine(x, GRAPH_Y, GRAPH_H, ILI9341_DARKGREY);
  }

  tft.setTextColor(ILI9341_WHITE);
  tft.setCursor(GRAPH_X, GRAPH_Y + GRAPH_H + 4);
  tft.print(F("100Hz"));
  tft.setCursor(GRAPH_X + 102, GRAPH_Y + GRAPH_H + 4);
  tft.print(F("1k"));
  tft.setCursor(GRAPH_X + GRAPH_W - 30, GRAPH_Y + GRAPH_H + 4);
  tft.print(F("4k"));
}

void displayLatestValues(const Measurement &m) {
  tft.fillRect(0, 195, 320, 45, ILI9341_BLACK);
  tft.setTextSize(1);
  tft.setTextColor(ILI9341_YELLOW);
  tft.setCursor(6, 198);
  tft.print(F("Posledni: "));
  tft.print(m.frequencyHz, 0);
  tft.print(F(" Hz"));

  tft.setCursor(6, 212);
  if (m.valid) {
    tft.setTextColor(ILI9341_GREEN);
    tft.print(F("|Z|="));
    if (m.magnitudeOhm >= 1000.0f) {
      tft.print(m.magnitudeOhm / 1000.0f, 2);
      tft.print(F(" kOhm"));
    } else {
      tft.print(m.magnitudeOhm, 1);
      tft.print(F(" Ohm"));
    }
    tft.setTextColor(ILI9341_MAGENTA);
    tft.setCursor(172, 212);
    tft.print(F("faze="));
    tft.print(m.phaseDeg, 1);
    tft.print(F(" deg"));
  } else {
    tft.setTextColor(ILI9341_RED);
    tft.print(F("MIMO ROZSAH / bez proudu"));
  }

  tft.setTextColor(ILI9341_WHITE);
  tft.setCursor(6, 227);
  tft.print(F("Serial: s=scan, c=continuous ON/OFF"));
}

void plotMeasurements() {
  drawGraphBackground();
  bool havePrevious = false;
  int16_t previousX = 0;
  int16_t previousY = 0;

  for (uint8_t i = 0; i < FREQ_COUNT; ++i) {
    if (!measurements[i].valid) {
      havePrevious = false;
      continue;
    }
    const int16_t x = graphXForIndex(i);
    const int16_t y = graphYForMagnitude(measurements[i].magnitudeOhm);
    if (havePrevious) {
      tft.drawLine(previousX, previousY, x, y, ILI9341_GREEN);
    }
    tft.fillCircle(x, y, 2, ILI9341_YELLOW);
    previousX = x;
    previousY = y;
    havePrevious = true;
  }
}

// ------------------------------- Sweep -------------------------------------
void runSweep() {
  tft.fillRect(0, 186, 320, 9, ILI9341_BLACK);
  tft.setTextSize(1);
  tft.setTextColor(ILI9341_CYAN);
  tft.setCursor(6, 187);
  tft.print(F("Merim..."));

  Serial.println();
  Serial.println(F("frequency_Hz;abs_Z_Ohm;phase_deg;Vin_DFT;Vout_DFT;status"));

  for (uint8_t i = 0; i < FREQ_COUNT; ++i) {
    measurements[i] = measureAtFrequency(TEST_FREQUENCIES_HZ[i]);
    const Measurement &m = measurements[i];

    Serial.print(m.frequencyHz, 2);
    Serial.print(';');
    if (m.valid) {
      Serial.print(m.magnitudeOhm, 3);
      Serial.print(';');
      Serial.print(m.phaseDeg, 3);
      Serial.print(';');
      Serial.print(m.vinMagnitude, 3);
      Serial.print(';');
      Serial.print(m.voutMagnitude, 3);
      Serial.println(F(";OK"));
    } else {
      Serial.print(F("NaN;NaN;"));
      Serial.print(m.vinMagnitude, 3);
      Serial.print(';');
      Serial.print(m.voutMagnitude, 3);
      Serial.println(F(";OUT_OF_RANGE"));
    }
  }

  plotMeasurements();
  displayLatestValues(measurements[FREQ_COUNT - 1]);

  tft.fillRect(0, 186, 320, 9, ILI9341_BLACK);
  tft.setTextColor(ILI9341_CYAN);
  tft.setCursor(6, 187);
  tft.print(continuousMode ? F("AUTO SWEEP") : F("MANUAL MODE"));

  lastSweepMs = millis();
}

// ------------------------------ Arduino API --------------------------------
void setup() {
  Serial.begin(115200);
  delay(300);

  analogWriteResolution(12);
  analogReadResolution(12);
  setDacMidpoint();

  tft.begin();
  tft.setRotation(1);
  drawHeader();
  drawGraphBackground();

  tft.setTextSize(1);
  tft.setTextColor(ILI9341_WHITE);
  tft.setCursor(6, 198);
  tft.print(F("Start mereni..."));

  Serial.println(F("Arduino Impedancni Analyzator " PROGRAM_VERSION));
  Serial.println(F("UNO R4: DAC=A0, VIN=A1, VOUT=A2, RREF=1000 Ohm"));
  Serial.println(F("Front-end: R2/R3=1k seriove, C1/C2=100pF RFI, D1/D2 clamp na GND."));
  Serial.println(F("Poznamka: s jednim RREF=1k je prakticky rozsah zhruba 100 Ohm az 10 kOhm."));
  Serial.println(F("Prikazy: s = jednorazovy sweep, c = prepnout automaticke mereni."));

  runSweep();
}

void loop() {
  if (Serial.available() > 0) {
    const char command = static_cast<char>(Serial.read());
    if (command == 's' || command == 'S') {
      runSweep();
    } else if (command == 'c' || command == 'C') {
      continuousMode = !continuousMode;
      Serial.print(F("Continuous mode: "));
      Serial.println(continuousMode ? F("ON") : F("OFF"));
      displayLatestValues(measurements[FREQ_COUNT - 1]);
    }
  }

  if (continuousMode && millis() - lastSweepMs >= 1500UL) {
    runSweep();
  }
}

Uživatelský avatar
Caster
Příspěvky: 521
Registrován: 11 zář 2019, 09:02

Re: Arduino jako měřič impedance

Příspěvek od Caster » 31 kvě 2026, 22:07

Ukázka, jak program měří 8-) .

Obrázek

Uživatelský avatar
JPLABS
Příspěvky: 68
Registrován: 28 pro 2025, 16:52
Bydliště: Praha
Kontaktovat uživatele:

Re: Arduino jako měřič impedance

Příspěvek od JPLABS » 01 čer 2026, 12:21

No jasně, tato ukázka je ze simulátoru, tipuju Tina od Texasu. Také Linear Technology měl simulátor. Analog Devices má též simulátor. Jenže toto vše nesimuluje impedanci, ale závislost hodnoty součástky na kmitočtu a to ještě v nízkých kmitočtech. Simulátory pro vysoké kmitočty jsou docela problém. Tam je lepší v reálu odměřit než simulovat.

Z té ukázky je vidět, že hodnota odporu resistoru (součástky) https://www.gme.cz/v/1486228/gym-cym-rm ... y-rezistor tj. reálného odporu se na kmitočtu nemění. Kdyby jsi měřil tentýž resitor v rozsahu od 100 do 200 Mhz, budeš žasnout co naměříš. :D
Co se týká kondenzátorů a indukčností, simulátory ukazují ideální součástku. Obvykle, ty lepší simulátory, nabízejí nějaký výběr součástky dle určitého výrobce. Tvůrci simulátoru vložili do parametrů součástky základní hodnoty náhradního zapojení. Například sériový odpor k indukčnosti. Pořád se ale měří závislost indukčnosti nebo kapacity na kmitočtu. Když by jsi v reálu měřil závislot indukčnosti nebo kapacity na frekvenci, zjistíš, že ty součástky mají při vyšších frekvencích tzv. resonanční kmitočet. V datasheetech výrobců indukčností se obvykle udává.
Hodnoty R, L nebo C není impedance, ale tyto změříš na normálním RLC můstku. Obvykle se měří při 1 kHz. Lepší můstku nabízejí měření ještě při 50 Hz, 100 Hz a také 400 Hz. Ty mikropočítačové pak umějí vypočítat činitel jakosti nebo ztrátový činitel.
RLC můstek lze samozřejmě udělat samodomo s Arduinem, ale ta elektronika můstku před Arduinem není jednoduchá, má-li to měřit přesně.
Na netu je hafo návodů na měření R, L a C pomocí Arduina. Měří na principu převodu kmitočet na napětí. Typicky známý "Zajícometr". Nedostatek Zajícometrů je, že měří jakžtakž v úzkém rozsahu hodnot součástek. Měl jsem Zajícometr půjčený na testování. V toleranci 1% měřil resistory od 100 Ohm do 1 MegaOhm. Pod a nad už byly chyby třeba 20%. U kondenzátorů měřil někde od 1nF do 400 nF. Pod a nad tento rozsah byly výsledky katastrofání.
U indukčností to byl vůbec malér.
Arduinisté si toto vyrábí, protože to je levné, něco to ukazuje a není pro ně důležité co to ukazuje. Hlavně, že to něco ukazuje. :lol:

Tak toto vše není měření impedance. Impedance, jak jsem už psal nahoře má reálnou a imaginární složku a zobrazuje se do Smithova diagramu. Pěkná bakalářská práce na toto téma je zde: https://www.vut.cz/www_base/zav_prace_s ... e_id=83963. Impedanci a admitanci lze simulovat v Matlabu, který umí vykreslit Smithův diagram.

V reálu si můžete skutečně doma s velmi malými náklady udělat měřič impedance pomocí nějakýho mikropočítače, třeba Arduina. V prvé řadě se musítte vypořádat se zobrazováním Smithova diagramu na grafickém displeji. Čím rozměrově větší bude a s čím větším rozlišením, tím lepší budete mít výsledky.
Dále si musíte sestavit adaptérek (3 resistory + 3 kondenzátory + 3 diody s malým Uf), na který přípojíte testovaný obvod, což nemusí být nutně jen resistor nebo indukčnost, ale může to být také anténa nebo LC článek, filtr atd atd. K tomuto adaptérku pak připojíte tři AD převodníky.
Dále potřebujete něčím měřit kmitočet sinusového signálu, který budete z generátoru pouštět do testovaného obvodu. Řádově se jedná o MHz až desítky MHz.
Pak potřebujete ještě generátor signálu, tak do desítek MHz a musí vám dávat čistý signál o amplitudě okolo 1V.

A nakonec zbývá napsat program, tedy v pojetí Arduinistů, vytvořit "kódy". Co budou "kódy" dělat?
Měřit vstupní kmitočet (pokud údaj o kmitočtu nebudete vkládat do Arduina ručně) a současně hodnoty ze třech AD převodníků, postupně krok za krokem přelaďovat oscilátor (jestli nechcete měřit jen na jednom kmitočtu). Z naměřených hodnot z AD převodníků vypočtete reálnou a imaginární složku impedance. Tyto hodnoty přepočtete do kartézských souřadnic pro váš grafický displej napojený na Arduino. A nakonec vykreslíte prázdný Smithův diagram a do něj barevně puntík (při měření na jednom kmitočtu) nebo křivku (při měření v kmitočtovém rozsahu) z naměřených údajů, které jste přepočetli fdo kartézských souřadnic.
Případně pak můžete celé odeslat na plotter a vykreslit na papír.

Já mám takovéto zařízení udělané. Místo mikropočítače používám 50 let starý počítač (z května roku 1976) HP9825A: https://www.hpmuseum.org/hp9825.htm, ke kterému jsem si dodělal 7" LCD barevný monitor. Na něm vykreslím Smithův diagram a data z měření. Počítač HP9825A má 16-bitový hybridní processor taktovaný 2MHz a 24 kByte paměti. Ale maká jak splašený a krásně měří. Udělám foto mého stroje - měřiče impedance a dám na něj odkaz, aby jste se mohli inspirovat. :lol: .
S časem se vše zhoršuje (zákon prof. Parkinsona)
Obrázek

Uživatelský avatar
Caster
Příspěvky: 521
Registrován: 11 zář 2019, 09:02

Re: Arduino jako měřič impedance

Příspěvek od Caster » 01 čer 2026, 13:10

Název simulátoru je uveden v levém horním rohu obrázku simulace "LTspice" ne Tina-TI i když ji mám také nainstalovánu a umím ji používat.

Smithův diagram samozřejmě umím a používám ho např. pro ladění drátového dipólu pro příjem signálu GPS. Používám také 3GHz 2.2 version Metal Case SAA2 NanoVNA V2 2.8".

Můj program lze stáhnout a snadno vyzkoušet 8-) .

Odpovědět

Kdo je online

Uživatelé prohlížející si toto fórum: Žádní registrovaní uživatelé a 1 host