/* ESP32 Oscilloscope Clock V1.2.1 Original Oscilloscope Clock Copyright © 2010 Johannes P.M. de Rie http://www.dutchtronix.com/ScopeClockH3-1-Enhanced.htm Original ESP32 Version by Mauro Pintus, 2018/05/25 The time code was broken (most likely by changes to the underlying API) and this great sketch deserved to get an update, so I ripped out the broken internet/NTP stuff and replaced it. The time also now automatically updates every hour, which will fix any drift issues (see also "interval" preference, which you might want to tweak for your particular chip/module - the hourly update will pick up any slack in your timings. Test! TesT! TEST!). I also ripped out Excel stuff and other bits, to simplify things, and made other minor improvements and fixes. This should now drop-in-and-go. NOTE: There is also a "FastDAC" version on-site, which is around 6.5 times faster. How to use: Set your SSID and Password, as well as NTP server address, below. Load this sketch onto an ESP32 board (one with DAC pins, i.e. not ESP32-Cam) using the Arduino IDE 1.8.7+ (1.8.19 is recommended - 2.* is a disaster!) or VSCode or whatever. Connect your oscilloscope channels to the ESP32; one channel to GPIO25 and the other channel to GPIO26. You obviously need a dual channel oscilloscope to do this.* * When buying a portable digital oscilloscope, the difference between a single and dual channel model will likely be around twenty bucks. SAVE UP! Or whatever it takes to upgrade to this level of tech. As well as being infinitely useful, the cool factorness of being able to run your scope as a *display* is well worth the extra time/effort = money required. ** After hours of searching and researching I personally opted for a DSO2512G. It would be a mistake to follow this recommendation without first figuring out what you need a scope /for/. Having said that, if you are in the market for a portable dual-channel scope for messing with MCUs and audio circuits, general electronics, wowing kids and what-not, the DSO2512G is silly money for the tech you get. And that music FFT is a killer feature! Connect the ground of the oscilloscope to the GND of the ESP32 board (if it isn't already - I have my scope charging on USB right now, which also works; as the other USB socket (of my computer) is connected to the UART adaptor, which is connected to the ESP32, and my knee-bone!). Put your Oscilloscope in XY mode. Adjust the vertical scale of the used channels to fit the clock. (Try around 500mV and 100us for a DSO - or hit "Auto"!) Enjoy Your new Oscilloscope Clock!!! :) Note: This definitely looks better on an analog scope, but I do enjoy the ethereal quality of the DSO clock. Playing with the timings can get you some fun, too. I'd rather have a digital scope for *so* many other reasons. In fact, having both is better! Additional notes: By default this sketch will use WiFi+NTP to ascertain the time. If you want to show "a clock" as opposed to "the clock", you can set a custom start time below (also comment out the line: "#define NTP") and the board will start with the specified time at every boot-up. This is handy for testing. To change the start time, modify the variables h,m,s below. Status is sent down the serial line, so you can see what's going on in your serial monitor @ 115200 baud; particularly useful at startup. corz.org @ 2023-1-1 */ #include #include #include #include "DataTable.h" // Prefs.. // Comment out this line to use a fixed start time. #define NTP // Fixed Start Time. If you're using an NTP server, ignore these prefs. // As it's an analog clock, we use 12 hour clock time, regardless.. int h=10; //Start Hour int m=8; //Start Minutes int s=37; //Start Seconds // havoc for your serial line.. // #define DEBUG #if defined NTP #include #include "time.h" #include "sntp.h" const char* ssid = "WiFi-SSID"; // Set you WiFi-SSID const char* password = "WiFi-password"; // Set you WiFi-password // NTP (Time) Server // Set this to your nearest NTP server.. //const char* ntpServer = "1.uk.pool.ntp.org"; // Use this when testing/hammering.. const char* ntpServer = "time1.google.com"; // Set this to your local timezone.. const char* time_zone = "GMT+0BST-1,M3.5.0/01:00:00,M10.5.0/02:00:00"; // This string will setup the time server negotiation with proper rules about // when and for how long daylight saving time occurs. // (Google this exact string for a list of all the others) // Here's one you can use to test it's working.. //const char* time_zone = "CST+6CDT,M3.2.0/2,M11.1.0/2"; // No more prefs to set. Enjoy! int status = WL_IDLE_STATUS; bool useRealTime = true; bool doneUpdate = false; #endif // Variables int lastx,lasty; unsigned long currentMillis = 0; unsigned long previousMillis = 0; int Timeout = 15; // WiFi time-out, in seconds. Must be a positive integer. const long interval = 996; // milliseconds, you might want to tweak this // to get a better accuracy. void setup() { Serial.begin(115200); // always use this speed for debug data! Serial.println("\n"); Serial.println("ESP32 Oscilloscope Clock v1.2"); dac_output_enable(DAC_CHANNEL_1); dac_output_enable(DAC_CHANNEL_2); #if defined NTP Serial.print("\nConnecting to Wi-Fi.."); WiFi.begin (ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); delay(500); Serial.print("."); Timeout--; if (Timeout==0) { useRealTime = false; Serial.print(" Timeout.\n"); break; } } //Serial.println(""); if (useRealTime && Timeout!=0) { Serial.print(" Connected\n"); if (useRealTime) { configTzTime(time_zone, ntpServer); Serial.print("Time: "); printLocalTime(); } else { useRealTime = false; Serial.println("Time functions disabled"); } } if (!useRealTime) { Serial.println("Using Fixed Time"); } #endif #if !defined NTP Serial.println("Using Fixed Time"); #endif // Serial.println(""); // magic! h=(h*5)+m/12; } void loop() { currentMillis = millis(); if (currentMillis - previousMillis >= interval) { previousMillis = currentMillis; s++; } if (s==60) { s=0; m++; if ((m==12)||(m==24)||(m==36)||(m==48)) { h++; } } if (m==60) { m=0; h++; } if (h==60) { h=0; } #if defined NTP // Update time hourly.. if (m==0 && s<=1 && !doneUpdate) { Serial.print("\nUpdated Time: "); printLocalTime(); h=(h*5)+m/12; doneUpdate = true; } if (m==1 && doneUpdate) { doneUpdate = false; } #endif // Optionals // PlotTable(DialDots,sizeof(DialDots),0x00,1,0); // PlotTable(TestData,sizeof(TestData),0x00,0,00); //Full // PlotTable(TestData,sizeof(TestData),0x00,0,11); //Without square // int i; // Serial.println("Out Ring"); //2 to back trace // for (i=0; i < 1000; i++) PlotTable(DialData,sizeof(DialData),0x00,2,0); // Serial.println("Diagonals"); //2 to back trace // for (i=0; i < 2000; i++) PlotTable(DialData,sizeof(DialData),0x00,0,0); PlotTable(DialData,sizeof(DialData),0x00,1,0); //2 to back trace PlotTable(DialDigits12,sizeof(DialDigits12),0x00,1,0);//2 to back trace PlotTable(HrPtrData, sizeof(HrPtrData), 0xFF,0,9*h); // 9*h PlotTable(MinPtrData,sizeof(MinPtrData),0xFF,0,9*m); // 9*m PlotTable(SecPtrData,sizeof(SecPtrData),0xFF,0,5*s); // 5*s #if defined DEBUG Serial.printf("\nHours:%d Minutes:%d Seconds:%d\n", h, m, s); #endif } // conditions probably redundant - I must check the compiler docs! #if defined NTP void printLocalTime() { struct tm timeinfo; if (!getLocalTime(&timeinfo)) { Serial.println("Failed to obtain time"); useRealTime = false; } else { Serial.println(&timeinfo, "%H:%M:%S, %A, %B %d %Y"); h=(int)timeinfo.tm_hour; m=(int)timeinfo.tm_min; s=(int)timeinfo.tm_sec; if (h > 12) h=h-12; // Pluralize.. (devs need to take the time to do this) String hpl = ""; String mpl = ""; String spl = ""; if (h != 1) hpl = "s"; if (m != 1) mpl = "s"; if (s != 1) spl = "s"; Serial.printf("Setting Analog Clock to %i Hour%s, %i Minute%s and %i Second%s.\n", h, hpl.c_str(), m, mpl.c_str(), s, spl.c_str()); } } #endif void PlotTable(byte *SubTable, int SubTableSize, int skip, int opt, int offset) { int i=offset; while (i dy) { // < 45 acc = (dx >> 1); for (; x1 <= x2; x1++) { Dot(x1, y1); acc -= dy; if (acc < 0) { y1++; acc += dx; } } } else { // > 45 acc = dy >> 1; for (; y1 <= y2; y1++) { Dot(x1, y1); acc -= dx; if (acc < 0) { x1++; acc += dy; } } } } else { // quadrant 2 byte dx = x1 - x2; if (dx > dy) { // < 45 acc = dx >> 1; for (; x1 >= x2; x1--) { Dot(x1, y1); acc -= dy; if (acc < 0) { y1++; acc += dx; } } } else { // > 45 acc = dy >> 1; for (; y1 <= y2; y1++) { Dot(x1, y1); acc -= dx; if (acc < 0) { x1--; acc += dy; } } } } } else { // quadrant 3 or 4 byte dy = y1 - y2; if (x1 < x2) { // quadrant 4 byte dx = x2 - x1; if (dx > dy) { // < 45 acc = dx >> 1; for (; x1 <= x2; x1++) { Dot(x1, y1); acc -= dy; if (acc < 0) { y1--; acc += dx; } } } else { // > 45 acc = dy >> 1; for (; y1 >= y2; y1--) { Dot(x1, y1); acc -= dx; if (acc < 0) { x1++; acc += dy; } } } } else { // quadrant 3 byte dx = x1 - x2; if (dx > dy) { // < 45 acc = dx >> 1; for (; x1 >= x2; x1--) { Dot(x1, y1); acc -= dy; if (acc < 0) { y1--; acc += dx; } } } else { // > 45 acc = dy >> 1; for (; y1 >= y2; y1--) { Dot(x1, y1); acc -= dx; if (acc < 0) { x1--; acc += dy; } } } } } } // Dot inline void Dot(int x, int y) { if (lastx!=x) { lastx=x; dac_output_voltage(DAC_CHANNEL_1, x); } if (lasty!=y) { lasty=y; dac_output_voltage(DAC_CHANNEL_2, y); } }