|
Arduino Nanıo |
x 1 | |
|
1N4148WTDIODES
|
x 4 | |
|
BUTTON1null
|
x 4 | |
|
1N5408 |
x 1 | |
![]() |
3306F-1-104Bourns Inc.
|
x 1 | |
![]() |
SFMC-107-01-S-DSAMTEC INC.
|
x 1 |
![]() |
Arduino nanoArduino
|
|
![]() |
arduino IDEArduino
|
Build a MIND-BLOWING Mini Oscilloscope at Home with EST Projects
Build a MIND-BLOWING Mini Oscilloscope at Home with EST Projects
Ever wondered how you can measure and visualize electronic signals right at your desk? With this DIY Mini Oscilloscope project, you can! In this guide, we will walk through building a simple yet powerful oscilloscope using affordable and easy-to-find components. This is the perfect project for electronics enthusiasts looking to take their DIY skills to the next level.
Let’s break down how you can make this MIND-BLOWING mini oscilloscope using an Arduino Nano, LM2596 for the power supply, buttons for user control, a custom PCB, and more!
Materials Required
Here’s what you’ll need for this project:
Arduino Nano: The main controller for signal processing.
LM2596-05 Buck Converter: For the power supply (to step down voltage).
0.96" OLED Display: To visualize the signals.
4 Push Buttons:
Button 1: Menu Select
Button 2: UP
Button 3: DOWN
Button 4: HOLD
Resistors:
390KΩ
100KΩ
104 Variable Resistor: To control input signals.
1n4148 or 1n4007 Diodes: For pull-up mode.
Power Socket
AC 2 Pin Terminal Block
PCB: Custom-designed PCB from PCBway.
Wires, Breadboard, Soldering tools (for assembly).
Circuit Design and Button Configuration
For this project, we will be using four buttons that act as the control interface for the oscilloscope:
Menu Select – Button to toggle between different functions.
Up – Increases values or moves up in the menu.
Down – Decreases values or moves down in the menu.
Hold – Freezes the current waveform on the display.
Button Pin Configuration
Each button will be connected to specific pins on the Arduino Nano and configured as INPUT_PULLUP to ensure reliable operation.
We’ll be using 4 buttons for control:
Button 1 (Menu Select): Wired to pin 8.
Button 2 (UP): Wired to pin 9.
Button 3 (DOWN): Wired to pin 10.
Button 4 (HOLD): Wired to pin 11.
Each button will use the INPUT_PULLUP mode in Arduino to ensure stable signal readings. Here’s how you’ll set them up in the code:
-----------------------------------------------------------------------------------------
pinMode(2, INPUT_PULLUP); // Button pressed interrupt (int.0 IRQ)
pinMode(8, INPUT_PULLUP); // Select button
pinMode(9, INPUT_PULLUP); // Up button
pinMode(10, INPUT_PULLUP); // Down button
pinMode(11, INPUT_PULLUP); // Hold button
pinMode(12, INPUT); // 1/10 attenuator (Off=High-Z, Enable=Output Low)
pinMode(13, OUTPUT); // LED for status indication
--------------------------------------------------------------------------------------------
By using INPUT_PULLUP, the buttons are kept in a high state when not pressed, reducing the chance of false triggering due to noise. When a button is pressed, the state changes to LOW, triggering the appropriate function in the code.
Signal Attenuation and Input Control
To allow the oscilloscope to handle different signal ranges, we’ll implement a 1/10 attenuator using pin 12. This will allow us to switch between high-impedance (Off) and low-impedance (Enable) states, enabling better signal control.
--------------------------------------------------------------------------------------------
pinMode(12, INPUT); // 1/10 attenuator (Off=High-Z, Enable=Output Low)
--------------------------------------------------------------------------------------------
Display Setup with OLED
Using a 0.96" OLED display is perfect for visualizing the signal data. You can use the Adafruit SSD1306 library to interface the OLED with the Arduino Nano.
Install the library and initialize the display in the code:
--------------------------------------------------------------------------------------------
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
void setup() {
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}
display.clearDisplay();
display.display();
}
--------------------------------------------------------------------------------------------
PCB Assembly
Design the PCB to make the connections tidy and compact. You can order a custom PCB from PCBway. It will include the necessary connections for the Arduino Nano, resistors, buttons, and power supply.
Once the PCB arrives, the components will be soldered as per the design.
Video Reference
#include <Wire.h> #include <Adafruit_GFX.h> //#include <Adafruit_SSD1306.h> #include <Adafruit_SH1106.h> // https://github.com/wonho-maker/Adafruit_SH1106 #include <EEPROM.h> #define SCREEN_WIDTH 128 // OLED display width #define SCREEN_HEIGHT 64 // OLED display height #define REC_LENG 200 // size of wave data buffer #define MIN_TRIG_SWING 5 // minimum trigger swing.(Display "Unsync" if swing smaller than this value // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) #define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin) //Adafruit_SSD1306 oled(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // device name is oled Adafruit_SH1106 oled(OLED_RESET); // use this when SH1106 // Range name table (those are stored in flash memory) const char vRangeName[10][5] PROGMEM = {"A50V", "A 5V", " 50V", " 20V", " 10V", " 5V", " 2V", " 1V", "0.5V", "0.2V"}; // Vertical display character (number of characters including \ 0 is required) const char * const vstring_table[] PROGMEM = {vRangeName[0], vRangeName[1], vRangeName[2], vRangeName[3], vRangeName[4], vRangeName[5], vRangeName[6], vRangeName[7], vRangeName[8], vRangeName[9]}; const char hRangeName[10][6] PROGMEM = {"200ms", "100ms", " 50ms", " 20ms", " 10ms", " 5ms", " 2ms", " 1ms", "500us", "200us"}; // Hrizontal display characters const char * const hstring_table[] PROGMEM = {hRangeName[0], hRangeName[1], hRangeName[2], hRangeName[3], hRangeName[4], hRangeName[5], hRangeName[6], hRangeName[7], hRangeName[8], hRangeName[9]}; const PROGMEM float hRangeValue[] = { 0.2, 0.1, 0.05, 0.02, 0.01, 0.005, 0.002, 0.001, 0.5e-3, 0.2e-3}; // horizontal range value in second. ( = 25pix on screen) int waveBuff[REC_LENG]; // wave form buffer (RAM remaining capacity is barely) char chrBuff[8]; // display string buffer char hScale[] = "xxxAs"; // horizontal scale character char vScale[] = "xxxx"; // vartical scale float lsb5V = 0.00566826; // sensivity coefficient of 5V range. std=0.00563965 1.1*630/(1024*120) float lsb50V = 0.05243212; // sensivity coefficient of 50V range. std=0.0512898 1.1*520.91/(1024*10.91) volatile int vRange; // V-range number 0:A50V, 1:A 5V, 2:50V, 3:20V, 4:10V, 5:5V, 6:2V, 7:1V, 8:0.5V, 9:0.2V volatile int hRange; // H-range nubmer 0:200ms, 1:100ms, 2:50ms, 3:20ms, 4:10ms, 5:5ms, 6;2ms, 7:1ms, 8:500us, 9;200us volatile int trigD; // trigger slope flag, 0:positive 1:negative volatile int scopeP; // operation scope position number. 0:Veratical, 1:Hrizontal, 2:Trigger slope volatile boolean hold = false; // hold flag volatile boolean switchPushed = false; // flag of switch pusshed ! volatile int saveTimer; // remaining time for saving EEPROM int timeExec; // approx. execution time of current range setting (ms) int dataMin; // buffer minimum value (smallest=0) int dataMax; // maximum value (largest=1023) int dataAve; // 10 x average value (use 10x value to keep accuracy. so, max=10230) int rangeMax; // buffer value to graph full swing int rangeMin; // buffer value of graph botto int rangeMaxDisp; // display value of max. (100x value) int rangeMinDisp; // display value if min. int trigP; // trigger position pointer on data buffer boolean trigSync; // flag of trigger detected int att10x; // 10x attenetor ON (effective when 1) float waveFreq; // frequency (Hz) float waveDuty; // duty ratio (%) void setup() { pinMode(2, INPUT_PULLUP); // button pussed interrupt (int.0 IRQ) pinMode(8, INPUT_PULLUP); // Select button pinMode(9, INPUT_PULLUP); // Up pinMode(10, INPUT_PULLUP); // Down pinMode(11, INPUT_PULLUP); // Hold pinMode(12, INPUT); // 1/10 attenuator(Off=High-Z, Enable=Output Low) pinMode(13, OUTPUT); // LED // oled.begin(SSD1306_SWITCHCAPVCC, 0x3C) { // select 3C or 3D (set your OLED I2C address) oled.begin(SH1106_SWITCHCAPVCC, 0x3C); // use this when SH1106 auxFunctions(); // Voltage measure (never return) loadEEPROM(); // read last settings from EEPROM analogReference(INTERNAL); // ADC full scale = 1.1V attachInterrupt(0, pin2IRQ, FALLING); // activate IRQ at falling edge mode startScreen(); // display start message } void loop() { setConditions(); // set measurment conditions digitalWrite(13, HIGH); // flash LED readWave(); // read wave form and store into buffer memory digitalWrite(13, LOW); // stop LED setConditions(); // set measurment conditions again (reflect change during measure) dataAnalize(); // analize data writeCommonImage(); // write fixed screen image (2.6ms) plotData(); // plot waveform (10-18ms) dispInf(); // display information (6.5-8.5ms) oled.display(); // send screen buffer to OLED (37ms) saveEEPROM(); // save settings to EEPROM if necessary while (hold == true) { // wait if Hold flag ON dispHold(); delay(10); } // loop cycle speed = 60-470ms (buffer size = 200) } void setConditions() { // measuring condition setting // get range name from PROGMEM strcpy_P(hScale, (char*)pgm_read_word(&(hstring_table[hRange]))); // H range name strcpy_P(vScale, (char*)pgm_read_word(&(vstring_table[vRange]))); // V range name switch (vRange) { // setting of Vrange case 0: { // Auto50V range att10x = 1; // use input attenuator break; } case 1: { // Auto 5V range att10x = 0; // no attenuator break; } case 2: { // 50V range rangeMax = 50 / lsb50V; // set full scale pixcel count number rangeMaxDisp = 5000; // vartical scale (set100x value) rangeMin = 0; rangeMinDisp = 0; att10x = 1; // use input attenuator break; } case 3: { // 20V range rangeMax = 20 / lsb50V; // set full scale pixcel count number rangeMaxDisp = 2000; rangeMin = 0; rangeMinDisp = 0; att10x = 1; // use input attenuator break; } case 4: { // 10V range rangeMax = 10 / lsb50V; // set full scale pixcel count number rangeMaxDisp = 1000; rangeMin = 0; rangeMinDisp = 0; att10x = 1; // use input attenuator break; } case 5: { // 5V range rangeMax = 5 / lsb5V; // set full scale pixcel count number rangeMaxDisp = 500; rangeMin = 0; rangeMinDisp = 0; att10x = 0; // no input attenuator break; } case 6: { // 2V range rangeMax = 2 / lsb5V; // set full scale pixcel count number rangeMaxDisp = 200; rangeMin = 0; rangeMinDisp = 0; att10x = 0; // no input attenuator break; } case 7: { // 1V range rangeMax = 1 / lsb5V; // set full scale pixcel count number rangeMaxDisp = 100; rangeMin = 0; rangeMinDisp = 0; att10x = 0; // no input attenuator break; } case 8: { // 0.5V range rangeMax = 0.5 / lsb5V; // set full scale pixcel count number rangeMaxDisp = 50; rangeMin = 0; rangeMinDisp = 0; att10x = 0; // no input attenuator break; } case 9: { // 0.5V range rangeMax = 0.2 / lsb5V; // set full scale pixcel count number rangeMaxDisp = 20; rangeMin = 0; rangeMinDisp = 0; att10x = 0; // no input attenuator break; } } } void writeCommonImage() { // Common screen image drawing oled.clearDisplay(); // erase all(0.4ms) oled.setTextColor(WHITE); // write in white character oled.setCursor(85, 0); // Start at top-left corner oled.println(F("av v")); // 1-st line fixed characters oled.drawFastVLine(26, 9, 55, WHITE); // left vartical line oled.drawFastVLine(127, 9, 3, WHITE); // right vrtical line up oled.drawFastVLine(127, 61, 3, WHITE); // right vrtical line bottom oled.drawFastHLine(24, 9, 7, WHITE); // Max value auxiliary mark oled.drawFastHLine(24, 36, 2, WHITE); oled.drawFastHLine(24, 63, 7, WHITE); oled.drawFastHLine(51, 9, 3, WHITE); // Max value auxiliary mark oled.drawFastHLine(51, 63, 3, WHITE); oled.drawFastHLine(76, 9, 3, WHITE); // Max value auxiliary mark oled.drawFastHLine(76, 63, 3, WHITE); oled.drawFastHLine(101, 9, 3, WHITE); // Max value auxiliary mark oled.drawFastHLine(101, 63, 3, WHITE); oled.drawFastHLine(123, 9, 5, WHITE); // right side Max value auxiliary mark oled.drawFastHLine(123, 63, 5, WHITE); for (int x = 26; x <= 128; x += 5) { oled.drawFastHLine(x, 36, 2, WHITE); // Draw the center line (horizontal line) with a dotted line } for (int x = (127 - 25); x > 30; x -= 25) { for (int y = 10; y < 63; y += 5) { oled.drawFastVLine(x, y, 2, WHITE); // Draw 3 vertical lines with dotted lines } } } void readWave() { // Record waveform to memory array if (att10x == 1) { // if 1/10 attenuator required pinMode(12, OUTPUT); // assign attenuator controle pin to OUTPUT, digitalWrite(12, LOW); // and output LOW (output 0V) } else { // if not required pinMode(12, INPUT); // assign the pin input (Hi-z) } switchPushed = false; // Clear switch operation flag switch (hRange) { // set recording conditions in accordance with the range number case 0: { // 200ms range timeExec = 1600 + 60; // Approximate execution time(ms) Used for countdown until saving to EEPROM ADCSRA = ADCSRA & 0xf8; // clear bottom 3bit ADCSRA = ADCSRA | 0x07; // dividing ratio = 128 (default of Arduino) for (int i = 0; i < REC_LENG; i++) { // up to rec buffer size waveBuff[i] = analogRead(0); // read and save approx 112us delayMicroseconds(7888); // timing adjustment if (switchPushed == true) { // if any switch touched switchPushed = false; break; // abandon record(this improve response) } } break; } case 1: { // 100ms range timeExec = 800 + 60; // Approximate execution time(ms) Used for countdown until saving to EEPROM ADCSRA = ADCSRA & 0xf8; // clear bottom 3bit ADCSRA = ADCSRA | 0x07; // dividing ratio = 128 (default of Arduino) for (int i = 0; i < REC_LENG; i++) { // up to rec buffer size waveBuff[i] = analogRead(0); // read and save approx 112us // delayMicroseconds(3888); // timing adjustmet delayMicroseconds(3860); // timing adjustmet tuned if (switchPushed == true) { // if any switch touched switchPushed = false; break; // abandon record(this improve response) } } break; } case 2: { // 50ms range timeExec = 400 + 60; // Approximate execution time(ms) ADCSRA = ADCSRA & 0xf8; // clear bottom 3bit ADCSRA = ADCSRA | 0x07; // dividing ratio = 128 (default of Arduino) for (int i = 0; i < REC_LENG; i++) { // up to rec buffer size waveBuff[i] = analogRead(0); // read and save approx 112us // delayMicroseconds(1888); // timing adjustmet delayMicroseconds(1880); // timing adjustmet tuned if (switchPushed == true) { // if any switch touched break; // abandon record(this improve response) } } break; } case 3: { // 20ms range timeExec = 160 + 60; // Approximate execution time(ms) ADCSRA = ADCSRA & 0xf8; // clear bottom 3bit ADCSRA = ADCSRA | 0x07; // dividing ratio = 128 (default of Arduino) for (int i = 0; i < REC_LENG; i++) { // up to rec buffer size waveBuff[i] = analogRead(0); // read and save approx 112us // delayMicroseconds(688); // timing adjustmet delayMicroseconds(686); // timing adjustmet tuned if (switchPushed == true) { // if any switch touched break; // abandon record(this improve response) } } break; } case 4: { // 10ms range timeExec = 80 + 60; // Approximate execution time(ms) ADCSRA = ADCSRA & 0xf8; // clear bottom 3bit ADCSRA = ADCSRA | 0x07; // dividing ratio = 128 (default of Arduino) for (int i = 0; i < REC_LENG; i++) { // up to rec buffer size waveBuff[i] = analogRead(0); // read and save approx 112us // delayMicroseconds(288); // timing adjustmet delayMicroseconds(287); // timing adjustmet tuned if (switchPushed == true) { // if any switch touched break; // abandon record(this improve response) } } break; } case 5: { // 5ms range timeExec = 40 + 60; // Approximate execution time(ms) ADCSRA = ADCSRA & 0xf8; // clear bottom 3bit ADCSRA = ADCSRA | 0x07; // dividing ratio = 128 (default of Arduino) for (int i = 0; i < REC_LENG; i++) { // up to rec buffer size waveBuff[i] = analogRead(0); // read and save approx 112μs // delayMicroseconds(88); // timing adjustmet delayMicroseconds(87); // timing adjustmet tuned if (switchPushed == true) { // if any switch touched break; // abandon record(this improve response) } } break; } case 6: { // 2ms range timeExec = 16 + 60; // Approximate execution time(ms) ADCSRA = ADCSRA & 0xf8; // clear bottom 3bit ADCSRA = ADCSRA | 0x06; // dividing ratio = 64 (0x1=2, 0x2=4, 0x3=8, 0x4=16, 0x5=32, 0x6=64, 0x7=128) for (int i = 0; i < REC_LENG; i++) { // up to rec buffer size waveBuff[i] = analogRead(0); // read and save approx 56us // delayMicroseconds(24); // timing adjustmet delayMicroseconds(23); // timing adjustmet tuned } break; } case 7: { // 1ms range timeExec = 8 + 60; // Approximate execution time(ms) ADCSRA = ADCSRA & 0xf8; // clear bottom 3bit ADCSRA = ADCSRA | 0x05; // dividing ratio = 16 (0x1=2, 0x2=4, 0x3=8, 0x4=16, 0x5=32, 0x6=64, 0x7=128) for (int i = 0; i < REC_LENG; i++) { // up to rec buffer size waveBuff[i] = analogRead(0); // read and save approx 28us // delayMicroseconds(12); // timing adjustmet delayMicroseconds(10); // timing adjustmet tuned } break; } case 8: { // 500us range timeExec = 4 + 60; // Approximate execution time(ms) ADCSRA = ADCSRA & 0xf8; // clear bottom 3bit ADCSRA = ADCSRA | 0x04; // dividing ratio = 16(0x1=2, 0x2=4, 0x3=8, 0x4=16, 0x5=32, 0x6=64, 0x7=128) for (int i = 0; i < REC_LENG; i++) { // up to rec buffer size waveBuff[i] = analogRead(0); // read and save approx 16us delayMicroseconds(4); // timing adjustmet // time fine adjustment 0.0625 x 8 = 0.5us(nop=0.0625us @16MHz) asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); } break; } case 9: { // 200us range timeExec = 2 + 60; // Approximate execution time(ms) ADCSRA = ADCSRA & 0xf8; // clear bottom 3bit ADCSRA = ADCSRA | 0x02; // dividing ratio = 4(0x1=2, 0x2=4, 0x3=8, 0x4=16, 0x5=32, 0x6=64, 0x7=128) for (int i = 0; i < REC_LENG; i++) { // up to rec buffer size waveBuff[i] = analogRead(0); // read and save approx 6us // time fine adjustment 0.0625 * 20 = 1.25us (nop=0.0625us @16MHz) asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); } break; } } } void dataAnalize() { // get various information from wave form int d; long sum = 0; // search max and min value dataMin = 1023; // min value initialize to big number dataMax = 0; // max value initialize to small number for (int i = 0; i < REC_LENG; i++) { // serach max min value d = waveBuff[i]; sum = sum + d; if (d < dataMin) { // update min dataMin = d; } if (d > dataMax) { // updata max dataMax = d; } } // calculate average dataAve = (sum + 10) / 20; // Average value calculation (calculated by 10 times to improve accuracy) // decide display's max min value if (vRange <= 1) { // if Autorabge(Range number <=1) rangeMin = dataMin - 20; // maintain bottom margin 20 rangeMin = (rangeMin / 10) * 10; // round 10 if (rangeMin < 0) { rangeMin = 0; // no smaller than 0 } rangeMax = dataMax + 20; // set display top at data max +20 rangeMax = ((rangeMax / 10) + 1) * 10; // round up 10 if (rangeMax > 1020) { rangeMax = 1023; // if more than 1020, hold down at 1023 } if (att10x == 1) { // if 10x attenuator used rangeMaxDisp = 100 * (rangeMax * lsb50V); // display range is determined by the data.(the upper limit is up to the full scale of the ADC) rangeMinDisp = 100 * (rangeMin * lsb50V); // lower depend on data, but zero or more } else { // if no attenuator used rangeMaxDisp = 100 * (rangeMax * lsb5V); rangeMinDisp = 100 * (rangeMin * lsb5V); } } else { // if fix range // Write necessary code here (none for now) } // Trigger position search for (trigP = ((REC_LENG / 2) - 51); trigP < ((REC_LENG / 2) + 50); trigP++) { // Find the points that straddle the median at the center ± 50 of the data range if (trigD == 0) { // if trigger direction is positive if ((waveBuff[trigP - 1] < (dataMax + dataMin) / 2) && (waveBuff[trigP] >= (dataMax + dataMin) / 2)) { break; // positive trigger position found ! } } else { // trigger direction is negative if ((waveBuff[trigP - 1] > (dataMax + dataMin) / 2) && (waveBuff[trigP] <= (dataMax + dataMin) / 2)) { break; } // negative trigger poshition found ! } } trigSync = true; if (trigP >= ((REC_LENG / 2) + 50)) { // If the trigger is not found in range trigP = (REC_LENG / 2); // Set it to the center for the time being trigSync = false; // set Unsync display flag } if ((dataMax - dataMin) <= MIN_TRIG_SWING) { // amplitude of the waveform smaller than the specified value trigSync = false; // set Unsync display flag } freqDuty(); } void freqDuty() { // detect frequency and duty cycle value from waveform data int swingCenter; // center of wave (half of p-p) float p0 = 0; // 1-st posi edge float p1 = 0; // total length of cycles float p2 = 0; // total length of pulse high time float pFine = 0; // fine position (0-1.0) float lastPosiEdge; // last positive edge position float pPeriod; // pulse period float pWidth; // pulse width int p1Count = 0; // wave cycle count int p2Count = 0; // High time count boolean a0Detected = false; // boolean b0Detected = false; boolean posiSerch = true; // true when serching posi edge swingCenter = (3 * (dataMin + dataMax)) / 2; // calculate wave center value for (int i = 1; i < REC_LENG - 2; i++) { // scan all over the buffer if (posiSerch == true) { // posi slope (frequency serch) if ((sum3(i) <= swingCenter) && (sum3(i + 1) > swingCenter)) { // if across the center when rising (+-3data used to eliminate noize) pFine = (float)(swingCenter - sum3(i)) / ((swingCenter - sum3(i)) + (sum3(i + 1) - swingCenter) ); // fine cross point calc. if (a0Detected == false) { // if 1-st cross a0Detected = true; // set find flag p0 = i + pFine; // save this position as startposition } else { p1 = i + pFine - p0; // record length (length of n*cycle time) p1Count++; } lastPosiEdge = i + pFine; // record location for Pw calcration posiSerch = false; } } else { // nega slope serch (duration serch) if ((sum3(i) >= swingCenter) && (sum3(i + 1) < swingCenter)) { // if across the center when falling (+-3data used to eliminate noize) pFine = (float)(sum3(i) - swingCenter) / ((sum3(i) - swingCenter) + (swingCenter - sum3(i + 1)) ); if (a0Detected == true) { p2 = p2 + (i + pFine - lastPosiEdge); // calucurate pulse width and accumurate it p2Count++; } posiSerch = true; } } } pPeriod = p1 / p1Count; // pulse period pWidth = p2 / p2Count; // palse width waveFreq = 1.0 / ((pgm_read_float(hRangeValue + hRange) * pPeriod) / 25.0); // frequency waveDuty = 100.0 * pWidth / pPeriod; // duty ratio } int sum3(int k) { // Sum of before and after and own value int m = waveBuff[k - 1] + waveBuff[k] + waveBuff[k + 1]; return m; } void startScreen() { // Staru up screen oled.clearDisplay(); oled.setTextSize(1); // at double size character oled.setTextColor(WHITE); oled.setCursor(55, 0); oled.println(F("Mini")); oled.setCursor(30, 20); oled.println(F("Oscilloscope")); oled.setCursor(55, 42); oled.println(F("v1.1")); oled.display(); delay(1500); oled.clearDisplay(); oled.setTextSize(1); // After this, standard font size } void dispHold() { // display "Hold" oled.fillRect(42, 11, 24, 8, BLACK); // black paint 4 characters oled.setCursor(42, 11); oled.print(F("Hold")); // Hold oled.display(); // } void dispInf() { // Display of various information float voltage; // display vertical sensitivity oled.setCursor(2, 0); // around top left oled.print(vScale); // vertical sensitivity value if (scopeP == 0) { // if scoped oled.drawFastHLine(0, 7, 27, WHITE); // display scoped mark at the bottom oled.drawFastVLine(0, 5, 2, WHITE); oled.drawFastVLine(26, 5, 2, WHITE); } // horizontal sweep speed oled.setCursor(34, 0); // oled.print(hScale); // display sweep speed (time/div) if (scopeP == 1) { // if scoped oled.drawFastHLine(32, 7, 33, WHITE); // display scoped mark at the bottom oled.drawFastVLine(32, 5, 2, WHITE); oled.drawFastVLine(64, 5, 2, WHITE); } // trigger polarity oled.setCursor(75, 0); // at top center if (trigD == 0) { // if positive oled.print(char(0x18)); // up mark } else { oled.print(char(0x19)); // down mark ↓ } if (scopeP == 2) { // if scoped oled.drawFastHLine(71, 7, 13, WHITE); // display scoped mark at the bottom oled.drawFastVLine(71, 5, 2, WHITE); oled.drawFastVLine(83, 5, 2, WHITE); } // average voltage if (att10x == 1) { // if 10x attenuator is used voltage = dataAve * lsb50V / 10.0; // 50V range value } else { // no! voltage = dataAve * lsb5V / 10.0; // 5V range value } if (voltage < 10.0) { // if less than 10V dtostrf(voltage, 4, 2, chrBuff); // format x.xx } else { // no! dtostrf(voltage, 4, 1, chrBuff); // format xx.x } oled.setCursor(98, 0); // around the top right oled.print(chrBuff); // display average voltage圧の平均値を表示 // oled.print(saveTimer); // use here for debugging // vartical scale lines voltage = rangeMaxDisp / 100.0; // convart Max voltage if (vRange == 1 || vRange > 4) { // if range below 5V or Auto 5V dtostrf(voltage, 4, 2, chrBuff); // format *.** } else { // no! dtostrf(voltage, 4, 1, chrBuff); // format **.* } oled.setCursor(0, 9); oled.print(chrBuff); // display Max value voltage = (rangeMaxDisp + rangeMinDisp) / 200.0; // center value calculation if (vRange == 1 || vRange > 4) { // if range below 5V or Auto 5V dtostrf(voltage, 4, 2, chrBuff); // format *.** } else { // no! dtostrf(voltage, 4, 1, chrBuff); // format **.* } oled.setCursor(0, 33); oled.print(chrBuff); // display the value voltage = rangeMinDisp / 100.0; // convart Min vpltage if (vRange == 1 || vRange > 4) { // if range below 5V or Auto 5V dtostrf(voltage, 4, 2, chrBuff); // format *.** } else { // no! dtostrf(voltage, 4, 1, chrBuff); // format **.* } oled.setCursor(0, 57); oled.print(chrBuff); // display the value // display frequency, duty % or trigger missed if (trigSync == false) { // If trigger point can't found oled.fillRect(92, 14, 24, 8, BLACK); // black paint 4 character oled.setCursor(92, 14); // oled.print(F("unSync")); // dosplay Unsync } else { oled.fillRect(90, 12, 25, 9, BLACK); // erase Freq area oled.setCursor(91, 13); // set display locatio if (waveFreq < 100.0) { // if less than 100Hz oled.print(waveFreq, 1); // display 99.9Hz oled.print(F("Hz")); } else if (waveFreq < 1000.0) { // if less than 1000Hz oled.print(waveFreq, 0); // display 999Hz oled.print(F("Hz")); } else if (waveFreq < 10000.0) { // if less than 10kHz oled.print((waveFreq / 1000.0), 2); // display 9.99kH oled.print(F("kH")); } else { // if more oled.print((waveFreq / 1000.0), 1); // display 99.9kH oled.print(F("kH")); } oled.fillRect(96, 21, 25, 10, BLACK); // erase Freq area (as small as possible) oled.setCursor(97, 23); // set location oled.print(waveDuty, 1); // display duty (High level ratio) in % oled.print(F("%")); } } void plotData() { // plot wave form on OLED long y1, y2; for (int x = 0; x <= 98; x++) { y1 = map(waveBuff[x + trigP - 50], rangeMin, rangeMax, 63, 9); // convert to plot address y1 = constrain(y1, 9, 63); // Crush(Saturate) the protruding part y2 = map(waveBuff[x + trigP - 49], rangeMin, rangeMax, 63, 9); // to address calucurate y2 = constrain(y2, 9, 63); // oled.drawLine(x + 27, y1, x + 28, y2, WHITE); // connect between point } } void saveEEPROM() { // Save the setting value in EEPROM after waiting a while after the button operation. if (saveTimer > 0) { // If the timer value is positive, saveTimer = saveTimer - timeExec; // Timer subtraction if (saveTimer < 0) { // if time up EEPROM.write(0, vRange); // save current status to EEPROM EEPROM.write(1, hRange); EEPROM.write(2, trigD); EEPROM.write(3, scopeP); } } } void loadEEPROM() { // Read setting values from EEPROM (abnormal values will be corrected to default) int x; x = EEPROM.read(0); // vRange if ((x < 0) || (9 < x)) { // if out side 0-9 x = 3; // default value } vRange = x; x = EEPROM.read(1); // hRange if ((x < 0) || (9 < x)) { // if out of 0-9 x = 3; // default value } hRange = x; x = EEPROM.read(2); // trigD if ((x < 0) || (1 < x)) { // if out of 0-1 x = 1; // default value } trigD = x; x = EEPROM.read(3); // scopeP if ((x < 0) || (2 < x)) { // if out of 0-2 x = 1; // default value } scopeP = x; } void auxFunctions() { // voltage meter function float voltage; long x; if (digitalRead(8) == LOW) { // if SELECT button pushed, measure battery voltage analogReference(DEFAULT); // ADC full scale set to Vcc while (1) { // do forever x = 0; for (int i = 0; i < 100; i++) { // 100 times x = x + analogRead(1); // read A1 pin voltage and accumulate } voltage = (x / 100.0) * 5.0 / 1023.0; // convert voltage value oled.clearDisplay(); // all erase screen(0.4ms) oled.setTextColor(WHITE); // write in white character oled.setCursor(20, 16); // oled.setTextSize(1); // standerd size character oled.println(F("Battery voltage")); oled.setCursor(35, 30); // oled.setTextSize(2); // double size character dtostrf(voltage, 4, 2, chrBuff); // display batterry voltage x.xxV oled.print(chrBuff); oled.println(F("V")); oled.display(); delay(150); } } if (digitalRead(9) == LOW) { // if UP button pushed, 5V range analogReference(INTERNAL); pinMode(12, INPUT); // Set the attenuator control pin to Hi-z (use as input) while (1) { // do forever, digitalWrite(13, HIGH); // flash LED voltage = analogRead(0) * lsb5V; // measure voltage oled.clearDisplay(); // erase screen (0.4ms) oled.setTextColor(WHITE); // write in white character oled.setCursor(26, 16); // oled.setTextSize(1); // by standerd size character oled.println(F("DVM 5V Range")); oled.setCursor(35, 30); // oled.setTextSize(2); // double size character dtostrf(voltage, 4, 2, chrBuff); // display batterry voltage x.xxV oled.print(chrBuff); oled.println(F("V")); oled.display(); digitalWrite(13, LOW); // stop LED flash delay(150); } } if (digitalRead(10) == LOW) { // if DOWN botton pushed, 50V range analogReference(INTERNAL); pinMode(12, OUTPUT); // Set the attenuator control pin to OUTPUT digitalWrite(12, LOW); // output LOW while (1) { // do forever digitalWrite(13, HIGH); // flush LED voltage = analogRead(0) * lsb50V; // measure voltage oled.clearDisplay(); // erase screen (0.4ms) oled.setTextColor(WHITE); // write in white character oled.setCursor(26, 16); // oled.setTextSize(1); // by standerd size character oled.println(F("DVM 50V Range")); oled.setCursor(35, 30); // oled.setTextSize(2); // double size character dtostrf(voltage, 4, 1, chrBuff); // display batterry voltage xx.xV oled.print(chrBuff); oled.println(F("V")); oled.display(); digitalWrite(13, LOW); // stop LED flash delay(150); } } } void uuPinOutputLow(unsigned int d, unsigned int a) { // 指定ピンを出力、LOWに設定 // PORTx =0, DDRx=1 unsigned int x; x = d & 0x00FF; PORTD &= ~x; DDRD |= x; x = d >> 8; PORTB &= ~x; DDRB |= x; x = a & 0x003F; PORTC &= ~x; DDRC |= x; } void pin2IRQ() { // Pin2(int.0) interrupr handler // Pin8,9,10,11 buttons are bundled with diodes and connected to Pin2. // So, if any button is pressed, this routine will start. int x; // Port information holding variable x = PINB; // read port B status if ( (x & 0x07) != 0x07) { // if bottom 3bit is not all Hi(any wer pressed) saveTimer = 5000; // set EEPROM save timer to 5 secnd switchPushed = true; // switch pushed falag ON } if ((x & 0x01) == 0) { // if select button(Pin8) pushed, scopeP++; // forward scope position if (scopeP > 2) { // if upper limit scopeP = 0; // move to start position } } if ((x & 0x02) == 0) { // if UP button(Pin9) pusshed, and if (scopeP == 0) { // scoped vertical range vRange++; // V-range up ! if (vRange > 9) { // if upper limit vRange = 9; // stay as is } } if (scopeP == 1) { // if scoped hrizontal range hRange++; // H-range up ! if (hRange > 9) { // if upper limit hRange = 9; // stay as is } } if (scopeP == 2) { // if scoped trigger porality trigD = 0; // set trigger porality to + } } if ((x & 0x04) == 0) { // if DOWN button(Pin10) pusshed, and if (scopeP == 0) { // scoped vertical range vRange--; // V-range DOWN if (vRange < 0) { // if bottom vRange = 0; // stay as is } } if (scopeP == 1) { // if scoped hrizontal range hRange--; // H-range DOWN if (hRange < 0) { // if bottom hRange = 0; // satay as is } } if (scopeP == 2) { // if scoped trigger porality trigD = 1; // set trigger porality to - } } if ((x & 0x08) == 0) { // if HOLD button(pin11) pushed hold = ! hold; // revers the flag } }

Build a MIND-BLOWING Mini Oscilloscope at Home with EST Projects
*PCBWay community is a sharing platform. We are not responsible for any design issues and parameter issues (board thickness, surface finish, etc.) you choose.

Raspberry Pi 5 7 Inch Touch Screen IPS 1024x600 HD LCD HDMI-compatible Display for RPI 4B 3B+ OPI 5 AIDA64 PC Secondary Screen(Without Speaker)
BUY NOW
ESP32-S3 4.3inch Capacitive Touch Display Development Board, 800×480, 5-point Touch, 32-bit LX7 Dual-core Processor
BUY NOW
Raspberry Pi 5 7 Inch Touch Screen IPS 1024x600 HD LCD HDMI-compatible Display for RPI 4B 3B+ OPI 5 AIDA64 PC Secondary Screen(Without Speaker)
BUY NOW- Comments(2)
- Likes(4)
-
Anthony Pedotto Feb 22,2025
-
Engineer Oct 30,2024
-
Engineer Oct 23,2024
-
Uber Blake Oct 20,2024
- 0 USER VOTES
- YOUR VOTE 0.00 0.00
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
More by Estiak Khan
-
Make a 16-Channel Home IoT project using NodeMCU ESP32 WROOM Module If you're looking to build a versatile IoT device capable of controlling up to 16 appliances or devi...
-
DIY Pure Sine Wave Inverter Making At Home DIY Pure Sine Wave Inverter Making at Home Using EGS002 Module: A Complete GuideBuilding your own pu...
-
Build a MIND-BLOWING Mini Oscilloscope at Home with EST Projects Build a MIND-BLOWING Mini Oscilloscope at Home with EST ProjectsEver wondered how you can measure an...
-
Best IOT Project Making At Home using Microcontroller || PCBway https://www.pcbway.com/?from=technology4powerEvery electronic device needs PCBs. Are you looking for...
-
Universal Battery AutoCut Charge Controller with LCD - DIY at Home Are you looking to build a reliable and efficient battery AutoCut charge controller? Look no further...
-
PIC16F877A Trainer Board Build Your Microcomputer || Home-Made Microcontroller Master Trainer Tutorial Banglahttps://www.pcbw...
-
Autocut DC Mini IPS for DC 12V Load Autocut DC Mini IPS for DC 12V LoadIf you are looking for a reliable Autocut DC Mini IPS for DC 12V ...
-
Build a 12V Battery Autocut System with LCD Display Are you looking for an efficient and affordable way to protect and monitor your 12V battery system? ...
-
DIY Transistor Tester | Build Your Own LCR Meter at Home with Arduino Nano Are you fascinated by electronics and want to create your own tools for testing components? Building...
-
DIY 150W IPS Making At Home with Auto Changeover System | Mini IPS 2025 How to Make an Automatic 150W IPS Using Two Circuit ModulesAre you looking for a reliable and effici...
-
DIY RTC Digital Clock with Professional PCB Design In this blog post, we will guide you through making a DIY RTC Digital Clock using an Arduino UNO and...
-
🚀 DIY Long Backup Mini DC IPS | Make at Home Using a UPS Transformer! 💡 DIY Long Backup Mini DC IPS | Make at Home Using a UPS Transformer! IntroductionBuilding a Mini DC I...
-
How to Make a Simple DIY Mini DC Automatic IPS at Home Looking for a reliable power backup solution for your DC appliances? Building a Mini DC Automatic IP...
-
DIY Easily Make a Universal Any Type of Battery Charge Controller At Home DIY Easily Make a Universal Any Type of Battery Charge Controller At HomeIf you’re into DIY electron...
-
Make a 16-Channel Home IoT project using NodeMCU ESP32 WROOM Module If you're looking to build a versatile IoT device capable of controlling up to 16 appliances or devi...
-
DIY Pure Sine Wave Inverter Making At Home DIY Pure Sine Wave Inverter Making at Home Using EGS002 Module: A Complete GuideBuilding your own pu...
-
Build a MIND-BLOWING Mini Oscilloscope at Home with EST Projects Build a MIND-BLOWING Mini Oscilloscope at Home with EST ProjectsEver wondered how you can measure an...
-
Best IOT Project Making At Home using Microcontroller || PCBway https://www.pcbway.com/?from=technology4powerEvery electronic device needs PCBs. Are you looking for...
-
Universal Battery AutoCut Charge Controller with LCD - DIY at Home Are you looking to build a reliable and efficient battery AutoCut charge controller? Look no further...
-
PIC16F877A Trainer Board Build Your Microcomputer || Home-Made Microcontroller Master Trainer Tutorial Banglahttps://www.pcbw...
-
Autocut DC Mini IPS for DC 12V Load Autocut DC Mini IPS for DC 12V LoadIf you are looking for a reliable Autocut DC Mini IPS for DC 12V ...
-
Build a 12V Battery Autocut System with LCD Display Are you looking for an efficient and affordable way to protect and monitor your 12V battery system? ...
-
DIY Transistor Tester | Build Your Own LCR Meter at Home with Arduino Nano Are you fascinated by electronics and want to create your own tools for testing components? Building...
-
DIY 150W IPS Making At Home with Auto Changeover System | Mini IPS 2025 How to Make an Automatic 150W IPS Using Two Circuit ModulesAre you looking for a reliable and effici...
-
DIY RTC Digital Clock with Professional PCB Design In this blog post, we will guide you through making a DIY RTC Digital Clock using an Arduino UNO and...
-
🚀 DIY Long Backup Mini DC IPS | Make at Home Using a UPS Transformer! 💡 DIY Long Backup Mini DC IPS | Make at Home Using a UPS Transformer! IntroductionBuilding a Mini DC I...
-
Modifying a Hotplate to a Reflow Solder Station
145 0 2 -
MPL3115A2 Barometric Pressure, Altitude, and Temperature Sensor
108 0 1 -
-
Nintendo 64DD Replacement Shell
173 0 1 -
V2 Commodore AMIGA USB-C Power Sink Delivery High Efficiency Supply Triple Output 5V ±12V OLED display ATARI compatible shark 100W
346 4 1 -
How to measure weight with Load Cell and HX711
413 0 3 -
-
Instrumentation Input, high impedance with 16 bit 1MSPS ADC for SPI
524 0 0
Thank you so much