Arduino Simple Oscilloscope
This example takes advantage of the µPanel capability to implement dynamic and real-time panels. The presented code implements a simple oscilloscope, with input channel and sample rate selectable by the user. The sample rate can be chosen between 1 Hz and 1 kHz.
Hardware
- Arduino UNO Board
- ESP-01 WiFi module (with µPanel Firmware)
- ESP-01 Breadboard adapter
- Breadboard wires (4 lines, Male-Female)
µPanel definition
The application implements 3 completely different panels: 1) splash screen, 2) settings and 3) oscilloscope panel.
1) Splash screen HCTML definition:
D!822;{^*30%80,100!822,411{ht2,000,1*14T:μPanel;}/3{*5T:Mobile Interactive;_T:Universal Panel;}_{*7T:Oscilloscope;_*6T#A33:for Arduino UNO;}}/20*15B0:START;
2) Settings panel HCTML definition:
Panel Header: D!822;/5*12{^tTk%90!611*15:SETTINGS;//
Box Macro Definition: K1:{p25!822,411-%90<Td,-8.5!822k%28^:?;_*12Tfb:Selected: ;M??:?;_|$
Box 1: J1(Channel,AN0)/{^S1!F00,800p10%20r20;|s1+.0T:0;|s1+.1T:1;|s1+.2T:2;|s1+.3T:3;|s1+.4T:4;|s1+.5T:5;}}_|/
Box 2: J1(Frequency,1000 Hz)/{%90^|s1+.6T:1;|s1+.7T:10;|s1+.8T:100;|s1+.9T:1000;}}}/
Panel Footer: 18*15B0%90!F44,822r20#FFF:START;
3) Oscilloscope panel HCTML definition:
D!822;/5*12{^t%95!611*13T:CHANNEL ;M1:;}/G0%95,70*12:::Samples:[V]:0F0:FFF:FFF:822:411;
//{^%95|B1%40!F44,822r20#FFF:SETTINGS;|B2%40!F44,822r20#FFF:EXIT;}
Arduino Code
#define FOREVER -1 // Constant for wait forever String Msg; // Received Message char Page = 0; // Current Application Panel char Channel = 0; // Selected analog input channel int Rate = 1000; // Selected sample rate void setup() { Serial.begin(57600); // Initialise serial delay(3000); // Let's the module start Serial.println(""); // Discarge old partial messages } /***************************************************** * This function waits a data message from the uPanel * * Input: timeout_ms time to wait for message * -1 for forever * Return: 0 = Timeout, 1 = Message received ******************************************************/ int WaitMessage(int timeout_ms) { int c; unsigned long entrytime = millis(); static char KeepBuffer = 0; if (!KeepBuffer) Msg = ""; // The buffer contains a partial message? KeepBuffer = 0; // if not, clear buffer. Clear keep-flag anyway. do { while ((c = Serial.read()) > '\n') Msg += (char) c; // Read incoming chars, if any, until new line if (c == '\n') // is message complete? { if (Msg.substring(0,1).equals("#")) return 1; // if it is a data message return 1 Msg = ""; // otherwise, wait for another one } } while ((timeout_ms < 0) || (millis()-entrytime < timeout_ms)); // has max time passed? KeepBuffer = 1; // We timed-out, so keep the received partial message return 0; // return 0 for timeout } void DisplaySplashScreen() // Send the splash screen cointaining the Start button { Serial.print("$P:D!822;{^*30%80,100!822,411{ht2,000,1*14T:μPanel;}/3{*5T:Mobile Interactive;_T:Universal Panel;}_"); Serial.println("{*7T:Oscilloscope;_*6T#A33:for Arduino UNO;}}/20*15B0:START;"); while (WaitMessage(FOREVER)) { if (Msg.substring(0,4).equals("#B0P")) { Page = 1; break;} // has start been presed? Change page and exit } } void DisplaySettings() { Serial.print("$P:D!822;/5*12{^tTk%90!611*15:SETTINGS;//K1:{p25!822,411-%90<Td,-8.5!822k%28^:?;_*12Tfb:Selected: ;M??:?;_|$"); Serial.print("J1(Channel,AN"); Serial.print(Channel,DEC); Serial.print(")/{^S1!F00,800p10%20r20;|s1+.0T:0;|s1+.1T:1;|s1+.2T:2;|s1+.3T:3;|s1+.4T:4;|s1+.5T:5;}}"); Serial.print("_|/J1(Frequency,"); Serial.print(Rate,DEC); Serial.println(" Hz)/{%90^|s1+.6T:1;|s1+.7T:10;|s1+.8T:100;|s1+.9T:1000;}}}/18*15B0%90!F44,822r20#FFF:START;"); while (WaitMessage(FOREVER)) { // Manage channel selection if (Msg.substring(0,9).equals("#.EVT:0:1")) { Channel = 0; Serial.println("#M0AN0"); } if (Msg.substring(0,9).equals("#.EVT:1:1")) { Channel = 1; Serial.println("#M0AN1"); } if (Msg.substring(0,9).equals("#.EVT:2:1")) { Channel = 2; Serial.println("#M0AN2"); } if (Msg.substring(0,9).equals("#.EVT:3:1")) { Channel = 3; Serial.println("#M0AN3"); } if (Msg.substring(0,9).equals("#.EVT:4:1")) { Channel = 4; Serial.println("#M0AN4"); } if (Msg.substring(0,9).equals("#.EVT:5:1")) { Channel = 5; Serial.println("#M0AN5"); } // Manage Rate selection if (Msg.substring(0,9).equals("#.EVT:6:1")) { Rate = 1; Serial.println("#M11 Hz"); } if (Msg.substring(0,9).equals("#.EVT:7:1")) { Rate = 10; Serial.println("#M110 Hz"); } if (Msg.substring(0,9).equals("#.EVT:8:1")) { Rate = 100; Serial.println("#M1100 Hz"); } if (Msg.substring(0,9).equals("#.EVT:9:1")) { Rate = 1000; Serial.println("#M11000 Hz"); } if (Msg.substring(0,4).equals("#B0P")) { Page = 2; break;} // has start been pressed? Change page and exit } } void DisplayOscilloscope() { // Send Oscilloscope panel Serial.print("$P:D!822;/5*12{^t%95!611*13T:CHANNEL ;M1:;}/G0%95,70*12:::Samples:[V]:0F0:FFF:FFF:822:411;"); Serial.println("//{^%95|B1%40!F44,822r20#FFF:SETTINGS;|B2%40!F44,822r20#FFF:EXIT;}"); int Interval = 1000 / Rate; // Compute the sampling interval unsigned int Counter = 0; // Reset sampling counter Serial.print("#M1AN"); // Update Oscilloscope panel header Serial.println(Channel,DEC); // with the selected channel while (1) { if (WaitMessage(Interval)) // Wait until it's time for a new sample or incoming data { if (Msg.substring(0,4).equals("#B1P")) { Page = 1; return;} // Settings button? exit if (Msg.substring(0,4).equals("#B2P")) { Page = 0; return;} // Exit button? exit } int Sample = analogRead(A0+Channel); // Acquire the analog input channel float Voltage = ((float) Sample)/1024.0 * 5.0; // Transform LSB into Voltage Serial.print("#G0P:"); // Add a point to the plot Serial.print(Counter++,DEC); // Send the sample number Serial.print(","); // separator Serial.println(Voltage,3); // Send the acquired voltage } } void loop() { // Display the correct panel page if (Page == 0) DisplaySplashScreen(); // Send Application Splash Screen if (Page == 1) DisplaySettings(); // Send Settings panel if (Page == 2) DisplayOscilloscope(); // Send Oscilloscope panel }