I've been playing around with an Arduino Uno and code from https://github.com/hartmk/OpenSerialOscilloscope to create a Dual Channel Oscilloscope. The GitHub project only works with a single channel, my intention would be to add a second channel as well as a few PWM outputs for reference.
So far I've managed to add the second channel, however, during testing only one channel works at a time. With my limited coding skills, I managed to get this far, but I'm stuck on getting both channels functional.
Does anyone mind having a glance at my code and advise what I have done wrong?
#include <avr/wdt.h> //for watchdog
//Arduino //ATMEGA
#define LedState 8 //pin 14
#define Vin1 A1 //pin 28
#define Vin2 A2 //pin 29
unsigned long int startTime=0;
unsigned long int tempTime=0;
bool prevLedState=false;
int timeValue=70;
// I N T E R F A C E
//Blink (ON-OFF) the selected led for specific time in millisecond
void BlinkLed(int led, int blTimes, int millitime){
for(int i=0;i<blTimes;i++){
digitalWrite( led,HIGH);
delay(millitime);
digitalWrite( led,LOW);
delay(millitime);
}//for
}//Blink Led
//Reverse the led state
void ReverseLedState(int led){
if(prevLedState==false){
prevLedState=true;
digitalWrite( led,HIGH);
}//if false
else{
prevLedState=false;
digitalWrite( led,LOW);
}//else
}//Reverse Led State
//Blink led without delay the microcontroller functional circle
void TimerBlinkLed(int maxTime){
if( (millis()-tempTime >maxTime) ){
tempTime=millis();
ReverseLedState(LedState);
}//if
}//Timer Blink Led
// C O M M U N I C A T I O N
//Use this class to build and oscilloscope object
class OSCobject{
String timeMs="null";
String ch1="null";
String ch2="null";
String msg;
//Set channel 1 value
public: void SetCH1(float val){
this->ch1=FloatToString(val,2);
}//SetCH1
//Set channel 2 value
public: void SetCH2(float val){
this->ch2=FloatToString(val,2);
}//SetCH2
//This function is used by the object to build the 'send package'.
private: void BuildPacket(){
// unsigned long int currtime=millis()-startTime;
this->timeMs=String(millis());
this->msg=String(
"T "+timeMs+" "
"CH1 "+String(ch1)+" "
"CH2 "+String(ch2)+" "
);
}//BuildPacket
//Send data via serial port
public: void OscillSend(){
char *temp;
this->BuildPacket();
temp=&this->msg[0];
Serial.println(temp);
// }//if data
return temp;
}//OscSend
};
// U T I L I T I E S
//Convert a floating value to String
String FloatToString(float flt, int presisionNum ){
unsigned int TempInt= flt; // get the integer part of float
float TempFlt= flt-TempInt; // get the demical part of float
unsigned int TempDec= TempFlt*(pow(10,presisionNum)); //get demical in presision
// int TempDec= TempFlt*100; //get demical in presision
String FtoStrTemp="";
FtoStrTemp= TempInt;
FtoStrTemp+= ",";
if(TempDec<10){ //if only one dec
// FtoStrTemp+= "0";
for(int i=0;i<(presisionNum-1);i++){ FtoStrTemp+= "0";}
}//if
FtoStrTemp+= TempDec;
return FtoStrTemp;
}//Float to String
// M E A S U R E M E N T S
//Calculate average of a number of measurments
float GetSample(int input,int sample){
if(sample==0){return 0.0;}
float measure=0.0;
float sum=0.0;
for(int i=0;i<sample;i++){
measure= analogRead(input);
sum= sum +measure;
// delay(1);
}//for
float stableMeasure= sum/sample;
return stableMeasure;
}//Get Sample
//Get average from voltage divider 1
float GetVoltageDv1(int input){
float measureVolt= GetSample(input,10);
const float MaxVolt=4.85;
const float encMaxVal=1023.0;
const float R1=60000;
const float R2=15000;
float Vs1= (measureVolt*(R1+R2))/R2;
float volt1= Vs1* (MaxVolt/encMaxVal);
return volt1;
}//GetVoltage
//Get average from voltage divider 2
float GetVoltageDv2(int input){
float measureVolt= GetSample(input,10);
const float MaxVolt=4.85;
const float encMaxVal=1023.0;
const float R3=60000;
const float R4=15000;
float Vs2= (measureVolt*(R3+R4))/R4;
float volt2= Vs2* (MaxVolt/encMaxVal);
return volt2;
}//GetVoltage
// S E T U P
void setup() {
Serial.begin(115200);
pinMode(LedState,OUTPUT); //State led pin-> output
pinMode(Vin1,INPUT); //VoltageDivider 1 input to ADC
pinMode(Vin2,INPUT); //VoltageDivider 2 input to ADC
digitalWrite(LedState,LOW); //Set led state
startTime=millis(); //Initillize start time
wdt_enable(WDTO_4S); //watchdog enabled (if not reset until 4 second pass, reset Microcontroller)
}//setup
// M a i n
void loop() {
wdt_reset(); //reset watchdog
OSCobject Osc; //Oscilloscope Object
float volt1= GetVoltageDv1(Vin1); //get volt 1
float volt2= GetVoltageDv2(Vin2); //get volt 2
Osc.SetCH1(volt1); //store volt value to channel 1
Osc.SetCH2(volt2); //store volt value to channel 2
Osc.OscillSend(); //build data and send that to serial
TimerBlinkLed(250); //blink 2 times at a second (ON 250ms, OFF 250ms)x2
if(Serial.available() > 0) { //if there is incoming data from open serial oscilloscope software
String incomingByte = Serial.readString(); //read string ()
int intVal= incomingByte.toInt(); // convert to int
if(intVal>0){timeValue=intVal;} // data= hardware delay (to set signal resolution)
}//while
delay(timeValue); //delay with last valid timeValue
//BlinkLed(LedState,1,200);
}//main
The output for Serial Data would be an Integer with Time + String1 Value + String2 Value. It's exactly the same in the Arduino Serial Monitor as the data parsed to the PC Application.
The problem I'm getting is that only one channel works at a time, this coincides with the Serial Data obtained and thus reflects back to the code. If for example, I use one channel it'll display perfectly, but the other channel will stay at a Zero Value. Next time I reboot the UNO or apply a test signal, then the other channel might work but the first stays at Zero.
It would seem like a Variable, String or Boolean value is stored and not updated or even ignored. I would assume this would be why the GitHub developer gave up on the project.
The following shows the Application working, Square Wave Input on Channel 2 and DC + Input on Channel 1. Serial Data in the bottom left corner, exactly the same as on the IDE Serial Plotter. However, this rarely happens that both Channels work at the same time. I have eliminated any hardware problems, thus I can only assume Coding itself. Could this be due to using String with little Memory?
You should understand that String is not a useful type with an UNO. That's because managed memory requires more RAM than available on the UNO. So you can get any random behavior of your code from only one reason. Make this->msg a char array and use the applicable C string functions with it.
I had a quick look at your code from post #1 and I think you could completely remove the OSCobject class and all its String handling. Its sole purpose seems to be to create a string of text characters that gets printed to the hardware serial port.
The format seems to be: "T ms CH1 v1 CH2 v2 " + CR + LF
Where ms is the integer value returned from millis(), v1 & v2 are computed voltages taking into account what appears to be an external potential divider made up of a 60K and a 15K resistor.
You can achieve the same result with several Serial.print() statements and a final serial.println(). You can even output a text float value if you alter the calculations so that, say, 4000 implies 4.000 volts, just by dividing the value by 1000, printing the integer result, printing a decimal point and then printing the value modulo 1000.
Of course, if you can recompile the source code for the PC app from the github page, then you can make it even simpler by just printing the millis value, channel 1 value and channel 2 value separated by commas (or spaces). The nice human readable T, CH1 and CH2 characters are really a waste of serial bandwidth if you wanted to increase the output of your UNO oscilloscope.
Thank you for the assistance thus far. I must confess, this is my first Arduino project, thus my knowledge of proper coding would be limited. My Raspberry Pi's ADC gave in so I stole my son's Arduino to run a similar application. I'm familiar with PHP and SQL so some code makes sense. With applications written in SQL and PHP memory was never a problem, and if it were I would upgrade the Servers. I grew up in the DOS and QBasic era, so all of this is new to me, especially running into memory problems.
As for the code above, I managed to use Serial.print() statements and serial.println() to parse the final data to the Application, I got this! This is similar to HTML and PHP and makes sense to me. I will try to reduce the code even further by using Concatenate.
As for the values obtained and currently used in Strings, I'm still a bit confused. I've tried using Integers, but noticed I don't get decimals. Byte and Char won't work as my data consist of a string of digits including factions, ideally, I would like double digits with two decimals for accuracy. I guess the most suitable way to get rid of String would be to use Floats?
Thank you for the input so far, I never would have thought of memory constraints. After reading about the Evils of Strings it's clear why it hogs memory. Now just to figure out how to use Floats properly and the code should be ready.
I managed to remove a few String arguments and this significantly improved performance. I'm currently getting samples at 12mS where I couldn't do this before and only managed to get samples at 70mS before the Arduino started to act up.
I'm more than happy with the current Sampling rate as it would suit my needs.
The best I can get fluctuates between 3-5mS/Measurement. This is more than enough to display a perfect Square Wave without the Rising and Falling edges being slewn.
I'm working on faultfinding a heavily populated SMD PC Board and it's difficult to track connections as it's a multilayered board. Prior to getting assistance from markd833 and DrDiettrich a square wave was measured and plotted in a Trapezoidal shape, this could throw me off as one might interpret that the injected square wave signal was exposed to a capacitor delaying the rising and falling edge.
I'm working in the lower Hz range to inject signals and plot the signal through the oscilloscope, so I'd like the signal to be as pure as possible not to draw faulty conclusions. I suspect a few components but can't say for sure what was affected as I'm using a PIC18F46K22 to measure several data points and basically acting as a PLC, however, one Voltage Sampling Circuit reads out Voltage at about 2/3rds of the Input voltage.
I initially ran the project on a Raspberry Pi, but by not protecting inputs with Zeners I damaged the ADC. I rarely work on electronics anymore so I donated my oscilloscope and signal generators to a local college 5 years ago as I'm not using them. Essentially this project was born out of necessity and I can live with a few bugs in the code and the system.
It sounds like ten bit ADC results are not really necessary - you could fiddle around a little and get faster ADC reads with lower resolution.
(I haven't played with this stuff for a long time, and I don't have access to that code anymore, so don't ask me!)
I'm happy with the code as is. I just need to inject a signal, measure with CH1 and follow the board with the CH2 probe to determine where the problem could be.
Seems like some parasitic drain pulling down the sampling voltage before it reaches the PIC. Input voltage is ~48VDC so somewhere it has to be drawn down lower for the PIC to register the correct voltage ratio, but by not matching the input voltage something has gone up in smoke. A voltage divider, Comparator, Shorted Capacitor, anything could be to blame at this point. The manufacturer has no intention to help apart from selling me a completely new board, so I'm going in blind.