It has been 7 years (!) since I posted my PIC18F2550 KS0108 Graphical LCD Oscilloscope code and schematics. I have long since taken the circuit apart, sold my PIC microcontrollers, and moved on in my life (as one can surmise from my most recent posts detailing my graduate and postdoctoral work). However, I still get inquiries about the Microchip PIC oscilloscope, so I decided to recreate it using a simpler setup using my Arduino Fio.
Here’s a short teaser video just to show that, yes, it works (going through a couple different sine wave frequencies, some random noise, etc. just to illustrate it working):
Click through the break to get more information on the setup.
I used an Arduino Fio board that I picked up from SparkFun.com (available at Amazon.com) and a small SPI graphical LCD board that I picked up for a few bucks at dx.com (SKU 153821, also apparently available at Amazon.com). Since I don’t have a soldering iron here, I had to improvise with some female to female cables, also purchased from dx.com (SKU 151650).
Dx.com describes the LCD as a 5V module, but the GLCD board designer’s page (mini12864) states otherwise (translated from Chinese via Google Translate):
Dimensions (L × W × H): 47mm × 38mm × 6mm (excluding pins)
LCD sight (L × W): 33.7mm × 33.5mm
LCD Active Display Area (L × W): 30.7mm × 23mm
Backlight: White LED backlight bracket
Operating voltage: 3.3V ~ 5.5V (built-in booster circuit, without pressure)
Control IC: UC1701
Display format: 128 × 64 rows
Display: Blue on White
So, I ordered one of the graphical LCDs, waited a few weeks for delivery (because dx.com is a notoriously slow shipper), received it, hooked it up, and tried out the following simple “Hello World” script to confirm that that the GLCD/FIO combo functioned:
And it worked!
Thankfully, the code I previously posted was written in C, so porting to the Arduino took only a few minutes. I took advantage of a great open source graphical LCD library (u8glib) to handle the brunt of the work and added a serial port menu for manipulating the various display parameters. One important difference between this project and the previous one: Since the Arduino Fio is a 3.3V device, it can only handle 0-3.3V inputs, limiting its utility as an “oscilloscope” without proper input protection/voltage scaling. However, the code is extremely portable, meaning that you should be able to program any other Arduino and have it up-and-running in no time.
Here’s another 2 videos of the oscilloscope in action:
And finally, here’s the Arduino sketch for the oscilloscope:
Here’s the Arduino sketch for the square wave generator shown in the second video:
nice!
Great job!! The best part is “However, the code is extremely portable, meaning that you should be able to program any other Arduino and have it up-and-running in no time.”
Maybe porting to an arduino mega/uno with a 128×64 display?
Arduino FIO > has same processor as the UNO > AtMega 328 ?
FIO only has an socket to place a XBee on it, for wireless coms.
Only, haven’t found any wiring schematics to hook things up…. yet.
Better make it work faster and more usable then converting to stupid arduinos where every dumbass can connect wires without any soldering knowledge
Piles give you the spasms again?
So are you saying that ‘Electronic’s Pro’s’ are ‘dumb ass’s for using Breadboard? This is a great little project, for teaching or as a starting point for something more powerful.
Thank you to the Arthur of this project…
Oops, i now see lines 27 through 34 of the main code show the connections !
HELLO FRIEND, excuse, but you have many mistakes in the code, I wonder if you will be able to correct it, thank you.
We are Openiumper,an open source hardware promoter from China,I am very honored that you are interested in our product mini12864 LCD module and using it.If you need any help,we are glad to serve you,and you can contact us for technical support at any time.
My email:zhangming@openjumper.com
Great Project 🙂
can i use the same code with arduino uno & GLCD 128×64 like this one
http://n.mtng.org/ele/arduino/oscillo.html
if there is changes – what i need to do to make the code compatibel with UNO & GLCD 128×64
habe Probleme:
Fehlernachricht:
U8glib\U8glib.cpp.o: In function ‘U8GLIB::initRW8Bit(_u8g_dev_t*, unsgned char,….
vielen Dank für Ihre Hilfe
Hi! Great project! How to alter the project under the display Nokia5110 (PCD8544)?
code for arduino mini pro on atmega328 + OLED Display 128*64
#include “U8glib.h”
#include
// Variables you might want to play with
byte useThreshold = 1; // 0 = Off, 1 = Rising, 2 = Falling
byte theThreshold = 128; // 0-255, Multiplied by voltageConst
unsigned int timePeriod = 200;// 0-65535, us or ms per measurement (max 0.065s or 65.535s)
byte voltageRange = 1; // 1 = 0-3.3V, 2 = 0-1.65V, 3 = 0-0.825V
//byte ledBacklight = 100;
boolean autoHScale = true; // Automatic horizontal (time) scaling
boolean linesNotDots = true; // Draw lines between data points
// Variables that can probably be left alone
const byte vTextShift = 3; // Vertical text shift (to vertically align info)
const byte numOfSamples = 100; // Leave at 100 for 128×64 pixel display
unsigned int HQadcReadings[numOfSamples];
byte adcReadings[numOfSamples];
byte thresLocation = 0; // Threshold bar location
float voltageConst = 0.0793650; // Scaling factor for converting 0-63 to V
float avgV = 0.0;
float maxV = 0.0;
float minV = 0.0;
float ptopV = 0.0;
float theFreq = 0;
const byte theAnalogPin = A0; // Data read pin
//const byte lcdLED = 6; // LED Backlight
//const byte lcdA0 = 7; // Data and command selections. L: command H : data
//const byte lcdRESET = 8; // Low reset
//const byte lcdCS = 9; // SPI Chip Select (internally pulled up), active low
//const byte lcdMOSI = 11; // SPI Data transmission
//const byte lcdSCK = 13; // SPI Serial Clock
// SW SPI:
//U8GLIB_MINI12864 u8g(lcdSCK, lcdMOSI, lcdCS, lcdA0, lcdRESET);
// HW SPI:
//U8GLIB_MINI12864 u8g(lcdSCK, lcdMOSI, lcdCS, lcdA0, lcdRESET);
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE); // I2C / TWI
// High speed ADC code
// From: http://forum.arduino.cc/index.php?PHPSESSID=e21f9a71b887039092c91a516f9b…
#define FASTADC 1
// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
void collectData(void) {
unsigned int tempThres = 0;
unsigned int i = 0;
if (autoHScale == true) {
// With automatic horizontal (time) scaling enabled,
// scale quickly if the threshold location is far, then slow down
if (thresLocation > 5*numOfSamples/8) {
timePeriod = timePeriod + 10;
} else if (thresLocation numOfSamples/2) {
timePeriod = timePeriod + 2;
} else if (thresLocation < numOfSamples/2) {
timePeriod = timePeriod - 2;
}
}
// Enforce minimum time periods
if (timePeriod < 35) {
timePeriod = 35;
}
// Adjust voltage contstant to fit the voltage range
if (voltageRange == 1) {
voltageConst = 0.0793650; // 0-5V
} else if (voltageRange == 2) {
voltageConst = 0.0261905; // 0-1.65V
} else if (voltageRange == 3) {
voltageConst = 0.0130952; //0-0.825V
}
// If using threshold, wait until it has been reached
if (voltageRange == 1) tempThres = theThreshold << 2;
else if (voltageRange == 2) tempThres = theThreshold <tempThres) && (i<32768)) i++;
i = 0; while ((analogRead(theAnalogPin)<tempThres) && (i<32768)) i++;
}
else if (useThreshold == 2) {
i = 0; while ((analogRead(theAnalogPin)<tempThres) && (itempThres) && (i<32768)) i++;
}
// Collect ADC readings
for (i=0; i 35)
delayMicroseconds(timePeriod-35);
}
for (i=0; i>4 >4 & 0b111111;
else adcReadings[i] = 0b111111;
} else if (voltageRange == 2) {
if (HQadcReadings[i]>>3 >3 & 0b111111;
else adcReadings[i] = 0b111111;
} else if (voltageRange == 3) {
if (HQadcReadings[i]>>2 >2 & 0b111111;
else adcReadings[i] = 0b111111;
}
// Invert for display
adcReadings[i] = 63-adcReadings[i];
}
// Calculate and display frequency of signal using zero crossing
if (useThreshold != 0) {
if (useThreshold == 1) {
thresLocation = 1;
while ((adcReadings[thresLocation]>2))) && (thresLocation(63-(theThreshold>>2))) && (thresLocation(63-(theThreshold>>2))) && (thresLocation<numOfSamples-1)) (thresLocation++);
thresLocation++;
while ((adcReadings[thresLocation]>2))) && (thresLocation<numOfSamples-1)) (thresLocation++);
}
theFreq = (float) 1000/(thresLocation * timePeriod) * 1000;
}
// Average Voltage
avgV = 0;
for (i=0; i<numOfSamples; i++)
avgV = avgV + adcReadings[i];
avgV = (63-(avgV / numOfSamples)) * voltageConst;
// Maximum Voltage
maxV = 63;
for (i=0; i<numOfSamples; i++)
if (adcReadings[i]<maxV) maxV = adcReadings[i];
maxV = (63-maxV) * voltageConst;
// Minimum Voltage
minV = 0;
for (i=0; iminV) minV = adcReadings[i];
minV = (63-minV) * voltageConst;
// Peak-to-Peak Voltage
ptopV = maxV - minV;
}
void handleSerial(void) {
char inByte;
char dataByte;
boolean exitLoop = false;
do {
// Clear out buffer
do {
inByte = Serial.read();
} while (Serial.available() > 0);
Serial.print("\nArduino LCD Oscilloscope\n");
Serial.print(" 1 - Change threshold usage (currently: ");
if (useThreshold == 0) Serial.print("Off)\n");
else if (useThreshold == 1) Serial.print("Rise)\n");
else if (useThreshold == 2) Serial.print("Fall)\n");
Serial.print(" 2 - Change threshold value (currently: ");
Serial.print(theThreshold, DEC); Serial.print(")\n");
Serial.print(" 3 - Change sampling period (currently: ");
Serial.print(timePeriod, DEC); Serial.print(")\n");
Serial.print(" 4 - Change voltage range (currently: ");
if (voltageRange == 1) Serial.print("0-5V)\n");
else if (voltageRange == 2) Serial.print("0-1.65V)\n");
else if (voltageRange == 3) Serial.print("0-0.825V)\n");
Serial.print(" 5 - Toggle auto horizontal (time) scaling (currently: ");
if (autoHScale == true) Serial.print("On)\n");
else if (autoHScale == false) Serial.print("Off)\n");
Serial.print(" 6 - Toggle line/dot display (currently: ");
if (linesNotDots == true) Serial.print("Lines)\n");
else if (linesNotDots == false) Serial.print("Dots)\n");
Serial.print(" 8 - Exit\n");
// Wait for input/response, refresh display while in menu
do {
collectData();
// Picture Display Loop
u8g.firstPage();
do { draw(); } while( u8g.nextPage() );
} while (Serial.available() == 0);
inByte = Serial.read();
if (inByte == '1') {
Serial.print("Change threshold usage\n");
Serial.print(" 0 - Off\n");
Serial.print(" 1 - Rise\n");
Serial.print(" 2 - Fall\n");
do { } while (Serial.available() == 0);
dataByte = Serial.read();
if (dataByte == '0') useThreshold = 0;
else if (dataByte == '1') useThreshold = 1;
else if (dataByte == '2') useThreshold = 2;
} else if (inByte == '2') {
Serial.print("Change threshold value (thresholds for 0-5V,0-1.65V,0-0.825V ranges)\n");
Serial.print(" 0 - 32 (0.41V, 0.21V, 0.10V)\n");
Serial.print(" 1 - 80 (1.04V, 0.52V, 0.26V)\n");
Serial.print(" 2 - 128 (1.66V, 0.83V, 0.41V)\n");
Serial.print(" 3 - 176 (2.28V, 1.14V, 0.57V)\n");
Serial.print(" 4 - 224 (2.90V, 1.45V, 0.72V)\n");
do { } while (Serial.available() == 0);
dataByte = Serial.read();
if (dataByte == '0') theThreshold = 32;
else if (dataByte == '1') theThreshold = 80;
else if (dataByte == '2') theThreshold = 128;
else if (dataByte == '3') theThreshold = 176;
else if (dataByte == '4') theThreshold = 224;
} else if (inByte == '3') {
Serial.print("Change sampling frequency\n");
Serial.print(" 0 - 28 kHz (35 us/sample)\n");
Serial.print(" 1 - 20 kHz (50 us/sample)\n");
Serial.print(" 2 - 10 kHz (100 us/sample)\n");
Serial.print(" 3 - 5 kHz (200 us/sample)\n");
Serial.print(" 4 - 2.5 kHz (400 us/sample)\n");
do { } while (Serial.available() == 0);
dataByte = Serial.read();
if (dataByte == '0') timePeriod = 35;
else if (dataByte == '1') timePeriod = 50;
else if (dataByte == '2') timePeriod = 100;
else if (dataByte == '3') timePeriod = 200;
else if (dataByte == '4') timePeriod = 400;
} else if (inByte == '4') {
Serial.print("Change voltage range\n");
Serial.print(" 1 - 0-5V\n");
Serial.print(" 2 - 0-1.65V\n");
Serial.print(" 3 - 0-0.825V\n");
do { } while (Serial.available() == 0);
dataByte = Serial.read();
if (dataByte == '1') voltageRange = 1;
else if (dataByte == '2') voltageRange = 2;
else if (dataByte == '3') voltageRange = 3;
} else if (inByte == '5') {
Serial.print("Toggle auto horizontal (time) scaling\n");
Serial.print(" 0 - Off\n");
Serial.print(" 1 - On\n");
do { } while (Serial.available() == 0);
dataByte = Serial.read();
if (dataByte == '0') autoHScale = false;
else if (dataByte == '1') autoHScale = true;
} else if (inByte == '6') {
Serial.print("Toggle line/dot display\n");
Serial.print(" 0 - Lines\n");
Serial.print(" 1 - Dots\n");
do { } while (Serial.available() == 0);
dataByte = Serial.read();
if (dataByte == '0') linesNotDots = true;
else if (dataByte == '1') linesNotDots = false;
} else if (inByte == '8') {
Serial.print("Bye!\n");
exitLoop = true;
}
} while (exitLoop == false);
}
void draw(void) {
int i;
char buffer[16];
u8g.setFont(u8g_font_micro);
// Draw static text
u8g.drawStr(0, 5+vTextShift, "Av");
u8g.drawStr(0, 11+vTextShift, "Mx");
u8g.drawStr(0, 17+vTextShift, "Mn");
u8g.drawStr(0, 23+vTextShift, "PP");
u8g.drawStr(0, 29+vTextShift, "Th");
u8g.drawStr(24, 35+vTextShift, "V");
u8g.drawStr(0, 41+vTextShift, "Tm");
u8g.drawStr(4, 47+vTextShift, "ms/div");
u8g.drawStr(20, 53+vTextShift, "Hz");
u8g.drawStr(0, 59+vTextShift, "R");
// Draw dynamic text
if (autoHScale == true) u8g.drawStr(124, 5, "A");
dtostrf(avgV, 3, 2, buffer);
u8g.drawStr(12, 5+vTextShift, buffer);
dtostrf(maxV, 3, 2, buffer);
u8g.drawStr(12, 11+vTextShift, buffer);
dtostrf(minV, 3, 2, buffer);
u8g.drawStr(12, 17+vTextShift, buffer);
dtostrf(ptopV, 3, 2, buffer);
u8g.drawStr(12, 23+vTextShift, buffer);
dtostrf(theFreq, 5, 0, buffer);
u8g.drawStr(0, 53+vTextShift, buffer);
if (useThreshold == 0) {
u8g.drawStr(12, 29+vTextShift, "Off");
} else if (useThreshold == 1) {
u8g.drawStr(12, 29+vTextShift, "Rise");
dtostrf((float) (theThreshold>>2) * voltageConst, 3, 2, buffer);
} else if (useThreshold == 2) {
u8g.drawStr(12, 29+vTextShift, "Fall");
dtostrf((float) (theThreshold>>2) * voltageConst, 3, 2, buffer);
}
u8g.drawStr(8, 35+vTextShift, buffer);
// Correctly format the text so that there are always 4 characters
if (timePeriod < 400) {
dtostrf((float) timePeriod/1000 * 25, 3, 2, buffer);
} else if (timePeriod < 4000) {
dtostrf((float) timePeriod/1000 * 25, 3, 1, buffer);
} else if (timePeriod < 40000) {
dtostrf((float) timePeriod/1000 * 25, 3, 0, buffer);
} else { // Out of range
dtostrf((float) 0.00, 3, 2, buffer);
}
u8g.drawStr(12, 41+vTextShift, buffer);
if (voltageRange == 1) {
u8g.drawStr(4, 59+vTextShift, "0-5");
} else if (voltageRange == 2) {
u8g.drawStr(4, 59+vTextShift, "0-1.65");
} else if (voltageRange == 3) {
u8g.drawStr(4, 59+vTextShift, "0-0.83");
}
// Display graph lines
u8g.drawLine((128-numOfSamples),0,(128-numOfSamples),63);
if (useThreshold != 0)
for (i=29; i>2));
for (i=0; i<63; i+=5) {
u8g.drawPixel(53,i);
u8g.drawPixel(78,i);
u8g.drawPixel(103,i);
u8g.drawPixel(127,i);
}
// Threshold bar
for (i=0; i<63; i+=3)
u8g.drawPixel(thresLocation+(128-numOfSamples),i);
// Draw ADC readings
if (linesNotDots == true) {
for (i=1; i<numOfSamples; i++) // Draw using lines
u8g.drawLine(i+(128-numOfSamples)-1,adcReadings[i-1],i+(128-numOfSamples),adcReadings[i]);
} else {
for (i=2; i 0) {
handleSerial();
}
// rebuild the picture after some delay
delay(100);
}
Hi,
Thanks for the very good article.
I’m trying to add an extra interval for 0-5V, but I don’t understand how the folowing lines needs to be changed.
if (voltageRange == 1) tempThres = theThreshold << 2;
if (voltageRange == 1) {
if (HQadcReadings[i]>>4 >4 & 0b111111;
else adcReadings[i] = 0b111111;
Interesing idea. But unfortunaly very slow response of display. It can work more as registrator. But for working as oscilloscope it needs for increase speed of scanning signal and responce of display/.
Hello Guys,
I currently having problems to compile this code to my arduino, result with following errors:
oscilloscope.ino:1:10: error: #include expects “FILENAME” or
oscilloscope:34: error: ‘U8GLIB_SSD1306_128X64’ does not name a type
oscilloscope.ino: In function ‘void collectData()’:
oscilloscope:51: error: ‘gt’ was not declared in this scope
oscilloscope:51: error: expected
)' before ';' token
;’ before ‘)’ tokenoscilloscope:51: error: expected
oscilloscope:53: error: expected
}' before 'else'
)’ before ‘numOfSamples’oscilloscope:53: error: expected
oscilloscope:55: error: ‘lt’ was not declared in this scope
oscilloscope:55: error: expected
)' before ';' token
;’ before ‘)’ tokenoscilloscope:55: error: expected
oscilloscope.ino: At global scope:
oscilloscope:60: error: expected unqualified-id before ‘if’
oscilloscope:60: error: expected unqualified-id before numeric constant
oscilloscope:64: error: expected unqualified-id before ‘if’
oscilloscope:66: error: expected unqualified-id before ‘else’
oscilloscope:68: error: expected unqualified-id before ‘else’
oscilloscope:72: error: expected unqualified-id before ‘if’
oscilloscope:72: error: expected constructor, destructor, or type conversion before ‘;’ token
oscilloscope:72: error: expected unqualified-id before numeric constant
oscilloscope:73: error: expected unqualified-id before ‘else’
oscilloscope:73: error: expected constructor, destructor, or type conversion before ‘)’ token
oscilloscope:73: error: expected constructor, destructor, or type conversion before ‘;’ token
oscilloscope:73: error: expected
)' before '&' token
)’ before ‘&’ tokenoscilloscope:73: error: expected unqualified-id before numeric constant
oscilloscope:74: error: expected constructor, destructor, or type conversion before '=' token
oscilloscope:74: error: expected unqualified-id before 'while'
oscilloscope:74: error: expected constructor, destructor, or type conversion before ')' token
oscilloscope:74: error: expected constructor, destructor, or type conversion before ';' token
oscilloscope:74: error: expected
oscilloscope:74: error: expected unqualified-id before numeric constant
oscilloscope:75: error: expected declaration before ‘}’ token
WordPress has mangled your code. Could you make it available as a zippped download?
Thank you for letting me know, I assume during an upgrade that it decided to escape lots of the quotes and greater than/less than signs, so I hope that I fixed them all.
I assembled this oscilloscope, there are several problems. Prompt with their decision.
1) When the test device, the graphics are cut in half (bottom). I had the idea that the scheme is not enough for the amplifier bias voltage. Someone did it? Can you give the scheme?
2) When summing up his hands to the analog input of Arduino, is responsible for receiving the signal, the oscilloscope shows the sine wave cheerfully 50-52Hz (full, without clipping). Indications on the screen is updated several times per second. If you hear any waveform, the oscilloscope begins to slow down (readings and schedule may hang for a few seconds, up to 7-10 seconds). After changing picture hangs again. If the disable generator and bring again hand, in 50% of cases, skokrost display returns. Can you suggest a solution to this problem?
Thank you
The sketch is missing now….
I’ve transitioned my blog to another host and there may be some broken elements, but I just double checked and it appears as though the github gists are correctly displayed in Safari and Chrome on a Mac?
Hello, how are you!
I know this post is from the year 2013, but would like to enteder as is done the part that makes the trigger oscilloscope, had as you explain to me, for I am with a small oscilloscope project but do not want to copy and mount, I I want to like the fact enteder program works.
I await a response.
att,
Evanuel Ribeiro
Edited code for Arduino nano here: https://github.com/Yourigh/Arduino_Nano_oscilloscope
Great, can you share the schematic diagram of the circuit?