Last Updated: 27/09/2021

Project 3 - Adjustable Desktop Power Supply and Oscilloscope

Projects >> Project 3 - Adjustable Desktop Power Supply and Oscilloscope

Adjustable Desktop Power Supply and Oscilloscope

Power Supply and Oscilloscope

I have had an old ATX power supply from an old desktop computer on my desk for some time. I wanted to convert it as a power supply for my Arduino projects as some of the projects I was building needed more than the normal bread board supply could provide. At the same time I had been working with some sensors that required 8.2v and it had been a bit of a pain setting that up so I decided as part of the project I would like an adjustable power supply.

At the same time I wanted some bright LED lighting to help me see when building projects and I also wanted a fan to suck away soldering fumes so decided both of these should be incorporated into the project. Also as the ATX power supply has a 5v output even when it is powered off, I decided to add a couple of USB charging sockets as well.

On top of that I also had been looking at buying an oscilloscope to help me with certain projects, but I suddenly became aware that the oscilloscope plus a power supply was going to take up a lot of bench space, so started looking into the possiblity of building both items in one case. Also, if the oscilloscope was to have a screen, why not make it a touch screen and get it to display the output voltages from the different supplies as well. So the basic v1 project goal is:

Quick Links

Power Supply

Oscilloscope

Oscilloscopev1 project code

Oscilloscope v3 project code

Oscilloscope v5 project and code using ads1256 external ADC

Project Overview

Although this is a home made project, I did want it to be as safe as possible so decided from the start to leave the power supply as standard. This would also make life easier if I ever needed to replace it.

Having used touch screen on previous projects I knew a 480x320 3.5" screen would be about the right size. However, I did not want to use a shield type of screen as I wanted to mount the screen on the front of the case so went for the break out version. The pin out is the same as the shield type, however they only support 3.3v so rather than messing about with Logic Level convertors to swap between 3.3v and 5.0v I decided to use one of my favourite boards, the Arduino Due

Power Supply

There are loads of tutorials online as well as many videos about how to convert an ATX power supply into a basic desktop power supply so I won't got into all the details about how to do the conversion and so will just give an overview.

SAFETY: I'm not a trained electrician on even an electronics expert so although I'm happy messing around with small DC voltages I decied to leave the power supply case unopened. Mains voltage is dangerous and I din't want to risk making changes within the power supply in case I made a mistake or in use something came loose and shorted the case. Especially dangerous if it made the case live.

The Case

At first I thought I would just build one out of plywood, however right now plywood is very expensive and my carpentry skills are not the best so it was just as cheap to order a case online.

I finished up ordering a:

Electronic Enclosures DIY Project Box Metal Junction Case Cover 250x190x110mm from ebay

I bought this based on the size of the 550W PSU I had lying around as it would have enough room for the PSU as well as space for the Arduino and shield stack.

The case arrived and after finding a scale diagram of the ATX power supply size and mounting points I cut out space for the PSU as well as space for a couple of USB poorts and a pair of banana plugs for a rear facing 12v supply.

I did the back first as I would learn from any mistakes I made...and I did learn. Not the best looking job but it worked.

Rear Panel ATX mount

This image shows the USB sockets, as they are for charging/power only they just have the 5v connected to them. The sockets are mounted on some vero board and that is mounted on top of some thick plastic vinyl I had to act as an insulator to the aluminium bracket.

USB port insulation

The USB sockets are USB Type A DIP Adapter Converter for 2.54mm PCB Breakout Board off ebay, about �3 for 5

Rear USB ports

I then built the switch to turn the power on and off. As the supply gives 5v when not turned on I connected that to a USB bracket I salvaed from an old PC. The LED is red/green so the idea was to have it showing red on standby and green when switched on. I decided to use a plug connector as I had some lying around in case I needed to remove the item....as you will see it was just as well I did...

Power switch and front USB circuit

Once connected to the Green power power on line and the purple 5v standby everything seemed to work really well and testing the different power lines gave the results I expected...I was happy.

DISASTER....

The power supply suddenly started to play up, it looked to me as if it was shorting out. The fans would turn for a fraction of a second and then stop. It was then the lights came on in my memeory rather than on the PSU..... Why had I removed it from my PC all those years ago. You've guessed it. It was faulty with an intermittent fault.....aargh!

Power Supply Mk2

I was building on a budget so put an advert on freecycle for old computers/PSU's. Within a couple of hours I had been offered a nice old computer and discovered it had a very nice Zalmann modular PSU.... I was back on track...

Or was I... As soon as I fitted it I realised that the new PSU was much longer and left very little space between the front of the case and the PSU. It became obvious that the mounting posts would be less than 1mm from the front of the PSU so a huge risk of short circuits. So that PSU was put aside for another project.

Power Supply Mk3

Thankfully I had a stream of offers of old computer cases so I finished up with an older but reliable 350W supply, more than enough for my needs so after some refitting and resoldering I was makng progress again.

Front of Case

I wanted 4 different power outputs with fuses, two sets of inputs for my oscilloscope, a volt/ammeter, touch screen, front mounted USB sockets, some LED's for future use, an adjuster for the variable power output and the on/off switch and LED. As you can see the only way to try and work the spacings was to place all the items on the font plate and move them around until I was happy.

Front panel mock up

After drilling, jigsawing and a lot of filing it all went together.

Front panel fitted out

The wiring is fairly simple for this first version although I hope to add some extras later but I needed it in use as soon as possible so I could start on the oscilloscope side of things.

Front panel wiring

Everything tested out OK except for the ammeter. No matter what I did the meter always read 55.2 amps.

After checking various circuit diagrams and tutorials assuming I was doing somthing wrong I discovered that my meter is missing a part. Noone fitted the shunt... although the seller gave me an instant refund I still need to order another as well as dig this one out. However, the good news is that the power supply even without the ammeter is working.

Front panel switched on

Next I need to build a plastic mount for the screen as it has some pins that will short against the case.

Finished front with touch screen surround

Finished Oscilloscope

Case front in Voltmeter mode

Voltmeter

Rear of case showing USB and 12v Output

Rear Outputs

Oscilloscope

Oscilloscopes are very complex bits of equipment and there is no way I was going to be able to build a fully featured version on my first attempt, and probably not in my second, third or fourth attempt. I have therefore limited my goals for version 1 with the goal that I can upgrade it over time.

As most of the stuff I work with is 0-18v DC and what I want to be able to track is what is happening in my circuits at various points. I therefore decided to limit my first attempt to being able to plot the signal for 0-30v DC (allows some headroom just in case).

Requirements

  1. I want to be able to plot voltage against time and from two sources at the same time.
  2. I need to be able to alter the frequency of the output so that I can make the output easier to read.
  3. All controls need to be on the touch screen to avoid having to add more buttons or dials to the control panel.
  4. It needs to be built in a way that is upgradable both in hardware and software.

Components

As I wanted a 3.5" touch screen that needs to be panel mounted I went for one of the break out boards. I ordered a 480x320 as I'd used them before but they delivered a 400x240 so might have to go with that. Using a 3.5" break out board usally means that it will require 3.3v, not only on the power supply but on the logic pins so an Arduino Due was the ideal board as it would give me plenty of extra pins to work with.

WARNING Attaching a 3.3v board to a 5v Mega, UNO or Nano will probably damage the screen as it's not just the power supply that needs to be 3.3v, but the digital pins as well.

3.5" Touch screen

Arduino Due

A prototype shield (Mega 2560 type).

Some protoboard to build and extra shield.

Various connectors.

If you do not know how to use or set up a touch screen there is a full Touch Screen tutorial in the components section

The pin out for my breakout board to my Arduino Due is:

Screen - Arduino
GND - GND
3.3v - 3.3v
CS - A3
X RS - A2
Y WR - A1
RD - A0
RST - A4
LED - GND
DB0 - 8
DB1 - 9
DB2 - 2
DB3 - 3
DB4 - 4
DB5 - 5
DB6 - 6
DB7 - 7

I built a connector onto a prototype board as there are other components to be added. In the picture below the 3.3v is being supplied from the Arduino Due however when finished the power will come from the main power supply.

Oscilloscope V1

If you do not know how to use or set up a touch screen there is a full Touch Screen tutorial in the components section

First version running, green line is reading a 3.3v supply, red line is reading a line that is being interrupted by capacitance from my hand on the wire.
The blue line is green (v1) - red (V2). The red and blue lines look to have duplicates lines but this is due the the frequency of the oscilloscope and the abilities of the camera coming into conflict.

Oscilloscopev1 code is available on the touch screen tutorial page

Version 1

The first version worked at voltages up to 3.3v as I did not have a voltage divider. It worked well as a proof of concept and showed me a number of areas of the code that could be improved as well as some circuit changes that will be needed going forwards.

Oscilloscope V3

Improvements to be made:

More efficient code in the void oscilloscope() function. Needs all floats removing to increase processing speed.

Voltage divider needed to increase the working range of the unit.

Power supply isolator. This is needed to allow measurements that are not relative to the power supply Gnd

The Good news

So I started with the goals above and removed as many floats as possible and using a simple voltage divider was able to read voltages up to 36v
I also changed the way the graph was drawn to use drawLine() instead of drawPixel() so that I could link the last point to the new point, this provided a much better looking graph, especially when dealing with square waves.

And now the bad news

I created a square wave generator, basically an Arduino UNO running the blinnk sketch at very high speeds as I wasn't convinced the graph was working well at higher frequencies and I was quickly proved right. It seemed to max out at about 3khz (3000 readings per second)and possibly less. I was a bit baffled as in theory I should have been able to reach 10KHz and then realised that the screen was also using the ADC for analogue readings for the touvh system.

As the touch system was running none stop the tall the touch screen analog pin reads meant I could not read and display at the frequencies I wanted. so plans had to be drawn up for version 4.

Click to Download code: Oscilloscopev3.ino

 

 
/*  13/09/2021
     oscilloscopev3
    Sketch goals over oscilloscopev3

    Attempt to improve oscilloscope graph...success

    Problems...can't get fast readings in current ADC mode


   Hardware
   Touch screen set up
   3.5" Open Smart 400x240 3.3v
   ST7793
   Arduino Due 3.3v

   Screen - Arduino
   GND  - GND
   3.3v - 3.3v
   CS   - A3
   X RS - A2
   Y WR - A1
   RD   - A0
   RST  - A4
   LED  - GND
   DB0  - 8
   DB1  - 9
   DB2  - 2
   DB3  - 3
   DB4  - 4
   DB5  - 5
   DB6  - 6
   DB7  - 7


   Pins used

  3-9 screen
  A0-A4 screen
  A5 Voltmeter/Oscilloscope
  A6 Voltmeter/Oscilloscope


*/

//Libraries required
#include 
//fonts
#include 
#include 

//make sure your screen is not commented out in MCUFriend_kpv.cpp
//My screen is a ST7793
#include 

//Create Touch screen object
MCUFRIEND_kbv tft;
//sets the rotation of the screen
//change the value depending how you want to use the screen, useful if you fit it in an enclosure the wrong way round.
//0 would be portrait...values 0-3 on 90 degree increments
#define TOUCH_ORIENTATION  1 //1 is landscape 400 wide x 240 high on my screen

//define some colours so don't need to remember hex codes
//see https://ee-programming-notepad.blogspot.com/2016/10/16-bit-color-generator-picker.html
//to create custom colours
#define BLACK   0x0000
#define BLUE    0x001F
#define RED     0xF800
#define GREEN   0x07E0
#define CYAN    0x07FF
#define MAGENTA 0xF81F
#define YELLOW  0xFFE0
#define WHITE   0xFFFF

//Now we know the display is working start on touch
//values below come from running Touch_shield_new.ino
//from the MCUFriend_kbv library examples
//input stuff starts here
int XP = 6, YP = 55, XM = 56, YM = 7;  //from calibration test

//Adafruit Touch screen library
#include          //
TouchScreen ts(XP, YP, XM, YM, 300);   //re-initialised after diagnose
TSPoint tp;//create a touch point object in the library

//Global variables
//Touch system
int xtouch;//true screen x position ZERO far left
int ytouch;//true screen y position ZERO top of screen
unsigned long touchTime;//stores the time in millis of the last touch
//alter this value to change the debounce sensitivity
int debounceSensitivity = 220;// too low gives false positives as will still be touching same point, too high and will become unresponsive
int touchEvent;//keeps track of touch event 1 = event to process

//Voltmeter/Oscilloscope
int voltAnalogPins[2] = {A5, A6}; //array to hold the analog pins
//float readingsVolts[2][2];//old and new values
unsigned long readingsVolts[2][2];//old and new values
//float voltageMultiplier = 0.0032;  //3.3v
//float voltageMultiplier = 0.0351;  //36v limit...voltage divider
//int voltageMultiplier = 351;
//int voltageMultiplier = 358;//after final calibration against multimeter..seems to be within 0.05V
int voltageMultiplier = 366;
unsigned long voltTimer;//millis() timer for voltmeter
int oscFrequency = 100;//Oscillator frequency HZ
unsigned long hzTimer; //time till next reading
int lastPixelPos[3];//the last position to draw a line from

//Display
int freqStep;//keeps position of next line on oscilloscope
int screenMode = 0;//keeps track of current screen
int oscMaxVolt = 5;
unsigned long screenChangeTimer;
//putting the screen boxes in an array like this allows them to be worked through and processed more efficiently
//when looking for what has been touched.
//Also allows you to set text as an offset of the various box dimensions
//Alterations to box size does no effect touch system
int screenboxes[][6] = {//x,y,w,h,screen mode,respond to touch
  {0, 0, 399, 239, 0, 0}, //screen 1 boundary box
  {2, 2, 40, 40, 0, 1}, //switch mode
  {50, 120, 100, 100, 0, 0}, //voltmenter left
  {250, 120, 100, 100, 0, 0}, //voltmeter right
  {0, 0, 0, 0, 0, 0}, //reserved for upgrades
  {0, 0, 0, 0, 0, 0}, //reserved
  {0, 0, 0, 0, 0, 0}, //reserved
  {0, 0, 0, 0, 0, 0}, //reserved
  {0, 0, 0, 0, 0, 0}, //reserved
  {0, 0, 0, 0, 0, 0}, ////reserved box 9
  ////////////////screenMode 1 oscilloscope
  {0, 0, 40, 40, 1, 1}, //10...switch mode
  {0, 42, 40, 40, 1, 1}, //11...V1 on off
  {0, 84, 40, 40, 1, 1}, //12...V2 on off
  {0, 126, 40, 40, 1, 1}, //13...v1/2 on off
  {0, 168, 40, 35, 1, 1}, //14...Auto range
  {0, 205, 40, 35, 1, 1}, //15...spare
  {42, 215, 45, 24, 1, 1}, //16 <<<
  {89, 215, 45, 24, 1, 1}, //17 <<
  {134, 215, 45, 24, 1, 1}, //18 <
  {261, 215, 45, 24, 1, 1}, //19 >>
  {308, 215, 45, 24, 1, 1}, //20 >>
  {355, 215, 45, 24, 1, 1}, //21 >
  {181, 215, 78, 24, 1, 0}, //22 frequency
  {0, 0, 0, 0, 1, 0}, //reserved for upgrades
  {0, 0, 0, 0, 1, 0}, //reserved
  {0, 0, 0, 0, 1, 0}, //reserved 25
  ///////////spare
  {0, 0, 0, 0, 0, 0}
};
int numscreenboxes = 26;//number of items in the array above
int buttonState[26];//holds the button states for all buttons 0 off 1 on

//builds the initial voltmeter screen, switch mode button, should contain 2 voltmeters
void buildscreen0() {
  int q;
  if (millis() > screenChangeTimer) {
   screenChangeTimer = millis() + 200;

  //make the whole screen black as a back ground
  tft.fillScreen(BLACK);
  //400 x 240 screen
  //using some downloadable fonts as they display better
  tft.setFont(&FreeSans12pt7b);
  tft.setTextColor(WHITE, BLACK);
  tft.drawRect(screenboxes[0][0], screenboxes[0][1], screenboxes[0][2], screenboxes[0][3], WHITE); // Boundary Box
  tft.drawRect(screenboxes[1][0], screenboxes[1][1], screenboxes[1][2], screenboxes[1][3], WHITE); // Mode Box
  tft.setCursor(screenboxes[1][0] + 9, screenboxes[1][1] + 27);
  tft.println("O");
  for (q = 2; q < 4; q++) {
    tft.drawRect(screenboxes[q][0], screenboxes[q][1], screenboxes[q][2], screenboxes[q][3], WHITE); // Mode Box
    tft.setCursor(screenboxes[q][0] + 23, screenboxes[q][1] + 75);
    tft.println("Volts");
  }
  screenMode = 0;
  //Serial.println(screenMode);
  }

}

//puts the volts into the voltmeter...rewritten to use less floats
//The rest of the screen stays constant, this keeps the screen speed fast
void voltmeter() {
  float displayVoltage;
  int q;
  if (millis() > voltTimer) {
    voltTimer = millis() + 100;//display voltage update every 100 milliseconds
    tft.setFont(&FreeSans12pt7b);
    tft.setTextColor(WHITE, BLACK);
    for (q = 0; q < 2; q++) {
      //voltAnalogPins[q][1] = analogRead(voltAnalogPins[q]);//get the values for the pins and store them in the array
      //readingsVolts[q][0] = voltAnalogPins[q][1] * voltageMultiplier;
      readingsVolts[q][0] = analogRead(voltAnalogPins[q]) * voltageMultiplier;

      //put a black background in to delete old text
      if (readingsVolts[q][0] != readingsVolts[q][1]) { //only update screen if something to update
        displayVoltage = readingsVolts[q][0] / 10000.00; //create float only if needed at last moment
        //black box to blank out last value
        tft.fillRect(screenboxes[q + 2][0] + 2, screenboxes[q + 2][1] + 2, screenboxes[q + 2][2] - 4, screenboxes[q + 2][3] / 2, BLACK); // text background
        tft.setCursor(screenboxes[q + 2][0] + 23, screenboxes[q + 2][1] + 25);
        tft.println(displayVoltage);
      }
      readingsVolts[q][1] = readingsVolts[q][0];
    }
  }
}

//Deal with screen touches
void screenPress() {
  processTouch();//call the processTouch function to see if there is anything to process
  //if there has been a touch event do something if the touch is within the button boundary
  if (touchEvent > 0) {
    processScreenPress();//find out if a button has been pressed
  }
}

void processTouch() {
  touchEvent = 0;//reset touchEvent
  if (millis() > touchTime) { //give them some time to move their finger off...saves processing loads of false touches
    touchTime = millis() + debounceSensitivity; //reset the timer
    //see if there is a touch in progress
    tp = ts.getPoint();
    //reset pins for next time we call the line above
    pinMode(YP, OUTPUT);      //restore shared pins
    pinMode(XM, OUTPUT);
    digitalWrite(YP, HIGH);  //because TFT control pins
    digitalWrite(XM, HIGH);

    if (tp.z > 0) {
      //Serial.println("tp.x=" + String(tp.x) + ", tp.y=" + String(tp.y) + ", tp.z =" + String(tp.z));

      //The values however do not match up with the pixel dimensions of the screen we are using
      //now to change the values to something usable
      //moving pointer to far left on my screen (box) gives Y value of 934
      //moving pointer to far right on my screen gives Y value of 130
      //934 - 134 = 800 points left to right screen is 400 pixels
      //moving pointer to top on my screen (box) gives X value of 162
      //moving pointer to top on my screen gives X value of 840
      //840 - 162 = 678 points top to bottom on a 240 pixel screen
      //Done this way to match Adafruit GFX library coordinates system

      xtouch = (800 - (tp.y - 130)) / 2; //800 minus value is because values are reversed, subtract the 130 to start at zero, divide by 2 because 800 touch points on a 400 pixel wide screen

      //equasion below would normally be (tp.x - 162))/2.8 but by multiplying by 10 we get rid of decimal places
      ytouch = (10 * (tp.x - 162)) / 28; //subtract the 162 to start at zero, divide by 2.8 because 678 touch points on a 240 pixel high screen
      //if the touch is within the limits of our screen
      if (xtouch > -1 && xtouch < 400 && ytouch > -1 && ytouch < 240) {
        touchEvent = 1;
      }
    }
  }
}

//find out if a button has been pressed
void processScreenPress() {
  int q;
  //loop thourgh the screenboxes array
  for (q = 0; q < numscreenboxes; q++) {
    //check to see if it's for this mode and responds to touch
    if (screenboxes[q][4] == screenMode && screenboxes[q][5] == 1) {
      //check to see if the touch is within an active box boundary
      if (xtouch >= screenboxes[q][0] && xtouch <= screenboxes[q][0] + screenboxes[q][2] && ytouch >= screenboxes[q][1] && ytouch <= screenboxes[q][1] + screenboxes[q][3]) {
        Serial.println(q);
        switchButtonProcess(q);
      }
    }
  }
}

//works out what the response to a button press should be
void switchButtonProcess(int myButton) {
  int q = myButton;
  switch (myButton) {
    case 1://switch to oscilloscope mode
      buildscreen1();//redraw screen in new mode
      break;
    case 10://switch to voltmeter
      buildscreen0();//redraw screen in new mode
      break;
    case 11:
      changeButtonState(myButton);
      drawButtonState(q, 4, 27, "V1", GREEN);
      break;
    case 12:
      changeButtonState(myButton);
      drawButtonState(q, 4, 27, "V2", RED);
      break;
    case 13:
      changeButtonState(myButton);
      drawButtonState(q, 4, 27, "1-2", CYAN);
      break;
    case 14:
      changeButtonState(myButton);
      drawButtonState(q, 4, 23, "R+", GREEN);
      if (oscMaxVolt < 36) {
        oscMaxVolt++;
        buildscreen1();
      }
      break;
    case 15:
      changeButtonState(myButton);
      drawButtonState(q, 4, 23, "R-", GREEN);
      if (oscMaxVolt > 1) {
        oscMaxVolt--;
        buildscreen1();
      }
      break;
    case 16:
      oscFrequency = oscFrequency + 1000;
      buildscreen1();
      break;
    case 17:
      oscFrequency = oscFrequency + 100;
      buildscreen1();
      break;
    case 18:
      oscFrequency = oscFrequency + 1;
      buildscreen1();
      break;
    case 19:
      oscFrequency = oscFrequency - 1;
      if (oscFrequency < 1) {
        oscFrequency = 1;
      }
      buildscreen1();
      break;
    case 20:
      oscFrequency = oscFrequency - 100;
      if (oscFrequency < 1) {
        oscFrequency = 1;
      }
      buildscreen1();
      break;
    case 21:
      oscFrequency = oscFrequency - 1000;
      if (oscFrequency < 1) {
        oscFrequency = 1;
      }
      buildscreen1();
      break;
    default:
      break;
  }
}

//draws buttons state and puts in text
void drawButtonState(int MyButton, int XOffset, int YOffset, String MyButtonText, int MyButtonColour) {
  int q = MyButton;//line just allows me to reuse same box code
  Serial.println(buttonState[MyButton]);
  Serial.println(MyButton);
  if (buttonState[MyButton] == 0) { //button off
    tft.fillRect(screenboxes[q][0], screenboxes[q][1], screenboxes[q][2], screenboxes[q][3], BLACK);
    tft.drawRect(screenboxes[q][0], screenboxes[q][1], screenboxes[q][2], screenboxes[q][3], WHITE);
    tft.setTextColor(WHITE, BLACK);
  } else { //button on
    tft.drawRect(screenboxes[q][0], screenboxes[q][1], screenboxes[q][2], screenboxes[q][3], BLACK);
    tft.drawRect(screenboxes[q][0], screenboxes[q][1], screenboxes[q][2], screenboxes[q][3], WHITE);
    tft.setTextColor(MyButtonColour, BLACK );
  }
  tft.setFont(&FreeSans12pt7b);
  tft.setCursor(screenboxes[q][0] + XOffset, screenboxes[q][1] + YOffset);
  tft.println(MyButtonText);

}

void changeButtonState(int myButton) {
  if (buttonState[myButton] < 1) {
    buttonState[myButton] = 1;
  } else {
    buttonState[myButton] = 0;
  }
}

//Oscilloscope screen
void buildscreen1() {
  int q;
  buttonState[14] = 0;
  buttonState[15] = 0;
  if(millis() > screenChangeTimer){
    screenChangeTimer = millis() + 200;

  //make the whole screen black as a back ground
  tft.fillScreen(BLACK);
  tft.setFont(&FreeSans12pt7b);
  tft.setTextColor(WHITE, BLACK);
  q = 10; //switch to voltmeter...doesn't have on off state as switches screen
  tft.drawRect(screenboxes[q][0], screenboxes[q][1], screenboxes[q][2], screenboxes[q][3], WHITE);
  tft.setCursor(screenboxes[1][0] + 9, screenboxes[1][1] + 27);
  tft.println("V");
  for (q = 11; q < 16; q++) {
    if (buttonState[q] < 1) {
      buttonState[q] = 1;
    } else {
      buttonState[q] = 0;
    }
    switchButtonProcess(q);
  }
  q = 16;
  drawButtonState(q, 2, 16, "<<<", YELLOW);
  q = 17;
  drawButtonState(q, 8, 16, "<<", YELLOW);
  q = 18;
  drawButtonState(q, 11, 16, "<", YELLOW);
  q = 19;
  drawButtonState(q, 11, 16, ">", YELLOW);
  q = 20;
  drawButtonState(q, 8, 16, ">>", YELLOW);
  q = 21;
  drawButtonState(q, 2, 16, ">>>", YELLOW);
  q = 22;
  refreshFreqDisp();
  drawXYAxis();
  screenMode = 1;
  Serial.println(screenMode);
  }
}
//draws Oscilloscope XY axis with values
void drawXYAxis() {
  //oscMaxVolt...max volts
  float voltFloat;
  tft.drawLine(89, 5, 389, 5, YELLOW);//X Axis
  tft.drawLine(89, 55, 389, 55, YELLOW);//X Axis
  tft.drawLine(89, 105, 389, 105, YELLOW);//X Axis
  tft.drawLine(89, 155, 389, 155, YELLOW);//X Axis
  tft.drawLine(89, 205, 389, 205, WHITE);//X Axis baseline
  tft.drawLine(88, 0, 88, 205, WHITE);//Y Axis
  tft.drawLine(390, 0, 390, 205, WHITE);//Y Axis
  tft.setFont(&FreeSans9pt7b);
  tft.setTextColor(WHITE, BLACK);
  tft.setCursor(42, 12);
  voltFloat = 1.00 * oscMaxVolt;
  tft.println(voltFloat);
  tft.setCursor(42, 62);
  voltFloat = 3.00 * (oscMaxVolt / 4.00);
  tft.println(voltFloat);
  tft.setCursor(42, 112);
  voltFloat = 1.00 * (oscMaxVolt / 2.00);
  tft.println(voltFloat);
  tft.setCursor(42, 162);
  voltFloat = 1.00 * (oscMaxVolt / 4.00);
  tft.println(voltFloat);
}

void refreshFreqDisp() {
  int q = 22;
  tft.fillRect(screenboxes[q][0], screenboxes[q][1], screenboxes[q][2], screenboxes[q][3], BLACK);
  tft.setFont(&FreeSans9pt7b);
  tft.setCursor(screenboxes[q][0] + 2, screenboxes[q][1] + 20);
  tft.println(oscFrequency);
  tft.setCursor(screenboxes[q][0] + 54, screenboxes[q][1] + 20);
  tft.println("Hz");
}

//This only redraws a single line at a time on the oscilloscope
void oscilloscope(){
  long pixelPos;
  if(micros() > hzTimer){
    //hzTimer = micros() + (1000000/oscFrequency);
      hzTimer = micros() + (100000/oscFrequency);
      tft.drawLine(freqStep+89,0,freqStep+89,204,BLACK);//blank out previous
      tft.drawPixel(freqStep+89,5,YELLOW);
      tft.drawPixel(freqStep+89,55,YELLOW);
      tft.drawPixel(freqStep+89,105,YELLOW);
      tft.drawPixel(freqStep+89,155,YELLOW);
      tft.drawPixel(freqStep+89,205,WHITE);
      if(buttonState[11] == 1){//V1

       
        readingsVolts[0][0] = analogRead(voltAnalogPins[0]) * voltageMultiplier;
        
      
        pixelPos = 205 - (((200/oscMaxVolt) * readingsVolts[0][0])/10000);
        //tft.drawPixel(freqStep+89,pixelPos,GREEN);
        //drawLine draws line from last position to new position
        //Massive improvement in square wave graphs
        tft.drawLine(freqStep+88,lastPixelPos[0],freqStep+89,pixelPos,GREEN);
        lastPixelPos[0] = pixelPos; 
      }else{
        readingsVolts[0][0] = 0;
      }
      if(buttonState[12] == 1){//V2
        //get the volts
        readingsVolts[1][0] = analogRead(voltAnalogPins[1]) * voltageMultiplier;
        //work out the pixel position
        pixelPos = 205 - (((200/oscMaxVolt) * readingsVolts[1][0])/10000);
        
        tft.drawLine(freqStep+88,lastPixelPos[1],freqStep+89,pixelPos,RED);
        lastPixelPos[1] = pixelPos;  
      }else{
        readingsVolts[1][0] = 0;
      }
      
      if(buttonState[13] == 1){//1-2

        pixelPos = 205 - (((200/oscMaxVolt) * (readingsVolts[0][0] - readingsVolts[1][0]))/10000);
        
        tft.drawLine(freqStep+88,lastPixelPos[1],freqStep+89,pixelPos,CYAN);
        lastPixelPos[2] = pixelPos;
        
         if(pixelPos < 0){
          pixelPos = 0;  
        }
        
        tft.drawPixel(freqStep+89,pixelPos,CYAN);  
      }
      
        freqStep++;
        if(freqStep > 299){
          freqStep = 0;//go back to start of display  
        }
    }  
}


void setup() {
  Serial.begin(9600);
  Serial.println("Oscilloscopev3...");
  Serial.println(" ");
  delay(2000);//experiment for when screen first powers up
  tft.reset();//trying to deal with the need to reset before scree works
  //Get the screens type
  uint16_t ID = tft.readID();
  Serial.print("ID = 0x");
  Serial.println(ID, HEX);
  delay(1000);
  tft.reset();
  delay(1000);
  tft.begin(ID);//start the screen
  tft.setRotation(TOUCH_ORIENTATION);
  //build the initial voltmeter screen
  buildscreen0();//builds the initial screen
}



void loop() {
  //process screen presses
  screenPress();
  //decide what to do based on current screen
  switch (screenMode) {
    case 0:
      voltmeter();//puts the volts in the display
      break;
    
      case 1://oscilloscope...updates the graph only
      oscilloscope();
      break;
    
    default:
      break;
  }

}

Oscilloscope V5

Download Zipped sketches

Goals:

Increased reading frequency without interfering with the screen
Read Voltages independent of Gnd so that I could read across components

With the goals I had set the first thing I needed was an external ADC. I managed to get an ads1256 and after searching around the internet for some code I found some that worked and could be modified for my needs.

The ads1256 is a 24 bit ADC so very accurate and can sample at up to 30khz (30,000 samples per second). I found this to be possible after altering some of the code, however once the code was combined with the screen and touch code, even after it had been optimised, I still found that I could not get the frequencies I wanted.

To get round this I added an extra screen mode so now the systems works as follows.

Mode 0: Duel voltmeters

Mode 1: Real time oscilloscope that starts to struggle at 500hz


Mode 2: Sampling Oscilloscope, this records as set of 6000 readings at a selected frequency and then displays them in a graph that can be scrolled. To get round the issues with the screen and the touch system when sampling all other items are turned off until the sample is complete. Even at 1000hz, that is only 6 seconds.

The results


Even though it has not worked out quite how I wanted, it is usable. Mode 1 is great for seeing instant changes in voltage on two separate channels and is the mode I use most of the time. if things get really fast I just record in mode 2. Obviously it's not as good as a proper oscilloscope but it has been an interesting project to work on.

Screen in recording mode at 20khz

Oscilloscope Recording

Two readings in recorded mode at 25khz (0v and 3.3v)

Oscilloscope Recording

Finished Unit

Oscilloscope V6

As with all projects they are never finished.

V6 goals are an external port for programming the Arduino without removing the case.

I also hope to try some alternate ADC's to try and increase the frequency that can be recorded and processed.

Additional Resource Links

Comments


This site has been designed to be child friendly, this means that comments cannot be added to videos or directly to the site.
To add a comment or ask a question please email the address in this image: and use Project 3 - Adjustable Desktop Power Supply and Oscilloscope as a reference.