diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a249a5b --- /dev/null +++ b/.gitignore @@ -0,0 +1,405 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore + +## Bionicbone Arduino Environment +# Visual Micro Folder +__vm +# Visual Studio Files +*.sln +*.vcxproj* + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml diff --git a/.gitignore_old b/.gitignore_old new file mode 100644 index 0000000..9491a2f --- /dev/null +++ b/.gitignore_old @@ -0,0 +1,363 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd \ No newline at end of file diff --git a/examples/Clock/Clock_demo/Clock_demo.ino b/examples/Clock/Clock_demo/Clock_demo.ino new file mode 100644 index 0000000..7d80e56 --- /dev/null +++ b/examples/Clock/Clock_demo/Clock_demo.ino @@ -0,0 +1,205 @@ +/* + An example analogue clock using a TFT LCD screen to show the time + use of some of the drawing commands with the library. + + For a more accurate clock, it would be better to use the RTClib library. + But this is just a demo. + + Uses fonts 1, 2, 4, 6 depending on radius of clock face + + Realistic radius is 45(cramped) to ??? + Largest screen tested was 800 x 480 8bit parallel interface and looked + good at a range of radius from 60 --> 240. + + Based on a sketch by Gilchrist 6/2/2014 1.0, and modified by Bodmer's + TFT_eSPI clock example. + Modified into a "Scalable" Widget by Bionicbone 13 June 2024. + + Make sure all the display driver and pin connections are correct by + editing the User_Setup.h file in the TFT_eSPI library folder. + + ######################################################################### + ###### DON'T FORGET TO UPDATE THE User_Setup.h FILE IN THE LIBRARY ###### + ######################################################################### + + Requires TFT_eSPI library here: + https://github.com/Bodmer/TFT_eSPI + + Requires widget library here: + https://github.com/Bodmer/TFT_eWidget + + Requires Time library by Paul Stoffregen - Install by Arduino Library Manager + NTP code by Paul Stoffregen +*/ + +const char ssid[] = "***********"; // your network SSID (name) +const char pass[] = "***********"; // your network password + + +#include +#include +#include +#include +#include +#include +#include +#include +#include // Hardware-specific library +#include // Widget library + + +TFT_eSPI tft = TFT_eSPI(); // Invoke custom library + +ClockWidget clk1 = ClockWidget(&tft); +ClockWidget clk2 = ClockWidget(&tft); +ClockWidget clk3 = ClockWidget(&tft); +ClockWidget clk4 = ClockWidget(&tft); +ClockWidget clk5 = ClockWidget(&tft); +ClockWidget clk6 = ClockWidget(&tft); + +#include + +// NTP Servers: +static const char ntpServerName[] = "us.pool.ntp.org"; +//static const char ntpServerName[] = "time.nist.gov"; +//static const char ntpServerName[] = "time-a.timefreq.bldrdoc.gov"; +//static const char ntpServerName[] = "time-b.timefreq.bldrdoc.gov"; +//static const char ntpServerName[] = "time-c.timefreq.bldrdoc.gov"; + +const int timeZone = 1; // Central European Time +//const int timeZone = -5; // Eastern Standard Time (USA) +//const int timeZone = -4; // Eastern Daylight Time (USA) +//const int timeZone = -8; // Pacific Standard Time (USA) +//const int timeZone = -7; // Pacific Daylight Time (USA) + +WiFiUDP Udp; +unsigned int localPort = 8888; // local port to listen for UDP packets + +time_t getNtpTime(); +void sendNTPpacket(IPAddress& address); + + +void setup(void) { + // Set up WiFi to get Internet Time + WiFi.begin(ssid, pass); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Udp.begin(localPort); + setSyncProvider(getNtpTime); + setSyncInterval(300); + + // Set up the TFT screen (SSD1963 controller) + tft.init(); + tft.setRotation(1); + tft.fillScreen(TFT_GREY); + + // Draw the city labels and clocks + tft.setTextColor(TFT_GREEN); + tft.drawCentreString("LONDON", 400, 10, 4); + clk1.newClock(400, 260, 220, TFT_GREEN, TFT_BLACK, TFT_WHITE, TFT_RED); + clk1.drawClockFace(); + + tft.setTextColor(TFT_BLUE); + tft.drawCentreString("NY City, USA", 100, 10, 4); + clk2.newClock(100, 120, 80, TFT_BLUE, TFT_BLACK, TFT_YELLOW, TFT_GREY); + clk2.drawClockFace(); + + tft.setTextColor(TFT_MAGENTA); + tft.drawCentreString("PARIS", 700, 10, 4); + clk3.newClock(700, 120, 80, TFT_MAGENTA, TFT_BLACK, TFT_GOLD, TFT_YELLOW); + clk3.drawClockFace(); + + tft.setTextColor(TFT_BLACK); + tft.drawCentreString("TOKYO", 100, 285, 4); + clk4.newClock(100, 390, 80, TFT_BLACK, TFT_WHITE, TFT_BLACK, TFT_RED); + clk4.drawClockFace(); + + tft.setTextColor(TFT_BLACK); + tft.drawCentreString("Hong Kong / Singapore", 720, 210, 2); + clk5.newClock(720, 290, 60, TFT_YELLOW, TFT_BLUE, TFT_YELLOW, TFT_RED); + clk5.drawClockFace(); + + tft.setTextColor(TFT_BLACK); + tft.drawCentreString("Sydney,", 735, 370, 2); + tft.drawCentreString("Australia", 750, 385, 2); + clk6.newClock(670, 420, 50, TFT_RED, TFT_YELLOW, TFT_RED, TFT_BLACK); + clk6.drawClockFace(); +} + +time_t prevDisplay = 0; // when the digital clock was displayed + +void loop() { + if (timeStatus() != timeNotSet) { + if (now() != prevDisplay) { //update the display only if time has changed + prevDisplay = now(); + clk1.updateClock(); // LONDON + clk2.updateClock(-5); // NY City + clk3.updateClock(1); // PARIS + clk4.updateClock(8); // TOKYO + clk5.updateClock(7); // HONG KONG / SINGAPORE + clk6.updateClock(9); // Sydney, Australia + } + } +} + + +/*-------- NTP code by Paul Stoffregen ----------*/ + +const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message +byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets + +time_t getNtpTime() +{ + IPAddress ntpServerIP; // NTP server's ip address + + while (Udp.parsePacket() > 0); // discard any previously received packets + Serial.println("Transmit NTP Request"); + // get a random server from the pool + WiFi.hostByName(ntpServerName, ntpServerIP); + Serial.print(ntpServerName); + Serial.print(": "); + Serial.println(ntpServerIP); + sendNTPpacket(ntpServerIP); + uint32_t beginWait = millis(); + while (millis() - beginWait < 1500) { + int size = Udp.parsePacket(); + if (size >= NTP_PACKET_SIZE) { + Serial.println("Receive NTP Response"); + Udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer + unsigned long secsSince1900; + // convert four bytes starting at location 40 to a long integer + secsSince1900 = (unsigned long)packetBuffer[40] << 24; + secsSince1900 |= (unsigned long)packetBuffer[41] << 16; + secsSince1900 |= (unsigned long)packetBuffer[42] << 8; + secsSince1900 |= (unsigned long)packetBuffer[43]; + return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR; + } + } + Serial.println("No NTP Response :-("); + return 0; // return 0 if unable to get the time +} + +// send an NTP request to the time server at the given address +void sendNTPpacket(IPAddress& address) +{ + // set all bytes in the buffer to 0 + memset(packetBuffer, 0, NTP_PACKET_SIZE); + // Initialize values needed to form NTP request + // (see URL above for details on the packets) + packetBuffer[0] = 0b11100011; // LI, Version, Mode + packetBuffer[1] = 0; // Stratum, or type of clock + packetBuffer[2] = 6; // Polling Interval + packetBuffer[3] = 0xEC; // Peer Clock Precision + // 8 bytes of zero for Root Delay & Root Dispersion + packetBuffer[12] = 49; + packetBuffer[13] = 0x4E; + packetBuffer[14] = 49; + packetBuffer[15] = 52; + // all NTP fields have been given values, now + // you can send a packet requesting a timestamp: + Udp.beginPacket(address, 123); //NTP requests are to port 123 + Udp.write(packetBuffer, NTP_PACKET_SIZE); + Udp.endPacket(); +} diff --git a/examples/Clock/Clock_demo/__vm/Compile.vmps.xml b/examples/Clock/Clock_demo/__vm/Compile.vmps.xml new file mode 100644 index 0000000..4fc747c --- /dev/null +++ b/examples/Clock/Clock_demo/__vm/Compile.vmps.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/Clock/Clock_demo/__vm/Configuration.Release.vmps.xml b/examples/Clock/Clock_demo/__vm/Configuration.Release.vmps.xml new file mode 100644 index 0000000..703e36c --- /dev/null +++ b/examples/Clock/Clock_demo/__vm/Configuration.Release.vmps.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/examples/Clock/Clock_demo/__vm/Upload.vmps.xml b/examples/Clock/Clock_demo/__vm/Upload.vmps.xml new file mode 100644 index 0000000..4fc747c --- /dev/null +++ b/examples/Clock/Clock_demo/__vm/Upload.vmps.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/src/TFT_eWidget.h b/src/TFT_eWidget.h index 2fe9f77..2792508 100644 --- a/src/TFT_eWidget.h +++ b/src/TFT_eWidget.h @@ -33,4 +33,5 @@ #include "widgets/meter/Meter.h" #include "widgets/graph/GraphWidget.h" #include "widgets/graph/TraceWidget.h" +#include "widgets/clock/ClockWidget.h" #endif \ No newline at end of file diff --git a/src/widgets/clock/ClockWidget.cpp b/src/widgets/clock/ClockWidget.cpp new file mode 100644 index 0000000..87a2df9 --- /dev/null +++ b/src/widgets/clock/ClockWidget.cpp @@ -0,0 +1,380 @@ + #include "Arduino.h" + #include "ClockWidget.h" + +// ######################################################################### +// clockWidget constructor +// ######################################################################### + ClockWidget::ClockWidget(TFT_eSPI* tft) + { + sx = 0; + sy = 0; + mx = 0; + my = 0; + hx = 0; + hy = 0; + sdeg = 0; + mdeg = 0; + hdeg = 0; + osx = 0; + osy = 0; + omx = 0; + omy = 0; + ohx = 0; + ohy = 0; + x0 = 0; + x1 = 0; + yy0 = 0; + yy1 = 0; + targetTime = 0; + hh = 12; + mm = 30; + ss = 0; + initial = 1; + + ctft = tft + + //ltx = 0; // Saved x coord of bottom of needle + //osx = 120, osy = 120; // Saved x & y coords + //old_analog = -999; // Value last displayed + //old_digital = -999; // Value last displayed + + //mx = 0; + //my = 0; + + //factor = 1.0; + //scaleStart = 0.0; + + //mlabel[8] = '\0'; + + //// Defaults + //strncpy(ms0, "0", 4); + //strncpy(ms1, "25", 4); + //strncpy(ms2, "50", 4); + //strncpy(ms3, "75", 4); + //strncpy(ms4, "100", 4); + + //redStart = 0; + //redEnd = 0; + //orangeStart = 0; + //orangeEnd = 0; + //yellowStart = 0; + //yellowEnd = 0; + //greenStart = 0; + //greenEnd = 0; + + //ntft = tft; + } + + void ClockWidget::drawClockFace(uint32_t xPosition, uint32_t yPosition, uint32_t radius, uint32_t digitColour, uint32_t faceColour) { + // Draw clock face + tft.fillCircle(xPosition, yPosition, radius, digitColour); //120 = 118 + tft.fillCircle(xPosition, yPosition, radius - (radius * 6 / 100), faceColour); //120 = 110 + + // Draw 12 lines + for (int i = 0; i < 360; i += 30) { + sx = cos((i - 90) * 0.0174532925); + sy = sin((i - 90) * 0.0174532925); + x0 = sx * (radius * 95 / 100) + xPosition; + yy0 = sy * (radius * 95 / 100) + yPosition; + x1 = sx * (radius * 83.33 / 100) + xPosition; + yy1 = sy * (radius * 83.33 / 100) + yPosition; + + tft.drawLine(x0, yy0, x1, yy1, digitColour); + } + + // Draw 60 dots + for (int i = 0; i < 360; i += 6) { + sx = cos((i - 90) * 0.0174532925); + sy = sin((i - 90) * 0.0174532925); + x0 = sx * (radius * 85 / 100) + xPosition; + yy0 = sy * (radius * 85 / 100) + yPosition; + + // Draw minute markers + tft.drawPixel(x0, yy0, digitColour); + + // Draw main quadrant dots + if (i == 0 || i == 180) tft.fillCircle(x0, yy0, 2, digitColour); + if (i == 90 || i == 270) tft.fillCircle(x0, yy0, 2, digitColour); + } + + // Text offset adjustment + uint32_t dialOffset = radius - (radius * 25 / 100); + uint8_t digitOffset = 0; + // Draw digits around clock perimeter + float xp = 0.0, yp = 0.0; // Use float pixel position for smooth AA motion + uint8_t font, singleDigitOffset, doubleDigitOffset, yPixelOffset; + tft.setTextColor(digitColour, faceColour); // Adding a background colour erases previous text automatically + for (uint32_t h = 1; h <= 12; h++) { + float sx1 = cos(((h * 360.0 / 12) - 90) * 0.0174532925); + float sy1 = sin(((h * 360.0 / 12) - 90) * 0.0174532925); + xp = sx1 * dialOffset + xPosition; + yp = sy1 * dialOffset + yPosition; + + /* + digitOffset and yPixelOffset for fonts: + Font 1 = 2 : 5 & yp - 3 + Font 2 = 3 : 6 & yp - 7 + Font 3 = N/A + Font 4 = 6 : 13 & yp - 10 + Font 5 = N/A + Font 6 = 12 : 26 & yp - 18 + */ + + if (radius >= 320) { + font = 6; singleDigitOffset = 12; doubleDigitOffset = 26; yPixelOffset = 18; + } + else if (radius < 69) { + font = 1; singleDigitOffset = 2; doubleDigitOffset = 5; yPixelOffset = 3; + } + else if (radius < 159) { + font = 2; singleDigitOffset = 3; doubleDigitOffset = 6; yPixelOffset = 7; + } + else if (radius < 320) { + font = 4; singleDigitOffset = 6; doubleDigitOffset = 13; yPixelOffset = 12; + } + digitOffset = (h < 10) ? singleDigitOffset : doubleDigitOffset; + tft.drawNumber(h, xp - digitOffset, yp - yPixelOffset, font); + } + targetTime = millis() + 1000; + } + + + void ClockWidget::updateClock(uint32_t xPosition, uint32_t yPosition, uint32_t radius, uint32_t hourMinColour, uint32_t secondsColour, uint32_t faceColour) { + // Pre-compute hand degrees, x & y coords for a fast screen update + sdeg = ss * 6; // 0-59 -> 0-354 + mdeg = mm * 6 + sdeg * 0.01666667; // 0-59 -> 0-360 - includes seconds + hdeg = hh * 30 + mdeg * 0.0833333; // 0-11 -> 0-360 - includes minutes and seconds + hx = cos((hdeg - 90) * 0.0174532925); + hy = sin((hdeg - 90) * 0.0174532925); + mx = cos((mdeg - 90) * 0.0174532925); + my = sin((mdeg - 90) * 0.0174532925); + sx = cos((sdeg - 90) * 0.0174532925); + sy = sin((sdeg - 90) * 0.0174532925); + + if (initial) { osx = xPosition; osy = yPosition; omx = xPosition; omy = yPosition; ohx = xPosition; ohy = yPosition; } + if (ss == 0 || initial) { + initial = 0; + // Erase hour and minute hand positions every minute + tft.drawLine(ohx, ohy, xPosition, yPosition, faceColour); + ohx = hx * (radius * 45 / 100) + xPosition; + ohy = hy * (radius * 45 / 100) + yPosition; + tft.drawLine(omx, omy, xPosition, yPosition, faceColour); + omx = mx * (radius * 68 / 100) + xPosition; + omy = my * (radius * 68 / 100) + yPosition; + } + + // Redraw new hand positions, hour and minute hands not erased here to avoid flicker + tft.drawLine(osx, osy, xPosition, yPosition, faceColour); + osx = sx * (radius * 69 / 100) + xPosition; + osy = sy * (radius * 69 / 100) + yPosition; + tft.drawLine(osx, osy, xPosition, yPosition, secondsColour); + tft.drawLine(ohx, ohy, xPosition, yPosition, hourMinColour); + tft.drawLine(omx, omy, xPosition, yPosition, hourMinColour); + tft.drawLine(osx, osy, xPosition, yPosition, secondsColour); + + tft.fillCircle(xPosition, yPosition, 3, secondsColour); + } + + +//// ######################################################################### +//// Draw meter meter at x, y and define full scale range & the scale labels +//// ######################################################################### +//void MeterWidget::analogMeter(uint16_t x, uint16_t y, float fullScale, const char *units, const char *s0, const char *s1, const char *s2, const char *s3, const char *s4) +//{ +// analogMeter(x, y, 0.0, fullScale, units, s0, s1, s2, s3, s4); +//} +// +//void MeterWidget::analogMeter(uint16_t x, uint16_t y, float startScale, float endScale, const char *units, const char *s0, const char *s1, const char *s2, const char *s3, const char *s4) +//{ +// // Save offsets for needle plotting +// mx = x; +// my = y; +// factor = 100.0/(endScale - startScale); +// scaleStart = startScale; +// +// strncpy(mlabel, units, 8); +// +// strncpy(ms0, s0, 4); +// strncpy(ms1, s1, 4); +// strncpy(ms2, s2, 4); +// strncpy(ms3, s3, 4); +// strncpy(ms4, s4, 4); +// +// // Meter outline +// ntft->fillRect(x, y, 239, 126, TFT_GREY); +// ntft->fillRect(x + 5, y + 3, 230, 119, TFT_WHITE); +// +// ntft->setTextColor(TFT_BLACK); // Text colour +// +// // Draw ticks every 5 degrees from -50 to +50 degrees (100 deg. FSD swing) +// for (int i = -50; i < 51; i += 5) { +// // Long scale tick length +// int tl = 15; +// +// // Coordinates of tick to draw +// float sx = cos((i - 90) * 0.0174532925); +// float sy = sin((i - 90) * 0.0174532925); +// uint16_t x0 = x + sx * (100 + tl) + 120; +// uint16_t y0 = y + sy * (100 + tl) + 140; +// uint16_t x1 = x + sx * 100 + 120; +// uint16_t y1 = y + sy * 100 + 140; +// +// // Coordinates of next tick for zone fill +// float sx2 = cos((i + 5 - 90) * 0.0174532925); +// float sy2 = sin((i + 5 - 90) * 0.0174532925); +// int x2 = x + sx2 * (100 + tl) + 120; +// int y2 = y + sy2 * (100 + tl) + 140; +// int x3 = x + sx2 * 100 + 120; +// int y3 = y + sy2 * 100 + 140; +// +// // Red zone limits +// if (redEnd > redStart) { +// if (i >= redStart && i < redEnd) { +// ntft->fillTriangle(x0, y0, x1, y1, x2, y2, TFT_RED); +// ntft->fillTriangle(x1, y1, x2, y2, x3, y3, TFT_RED); +// } +// } +// +// // Orange zone limits +// if (orangeEnd > orangeStart) { +// if (i >= orangeStart && i < orangeEnd) { +// ntft->fillTriangle(x0, y0, x1, y1, x2, y2, TFT_ORANGE); +// ntft->fillTriangle(x1, y1, x2, y2, x3, y3, TFT_ORANGE); +// } +// } +// +// // Yellow zone limits +// if (yellowEnd > yellowStart) { +// if (i >= yellowStart && i < yellowEnd) { +// ntft->fillTriangle(x0, y0, x1, y1, x2, y2, TFT_YELLOW); +// ntft->fillTriangle(x1, y1, x2, y2, x3, y3, TFT_YELLOW); +// } +// } +// +// // Green zone limits +// if (greenEnd > greenStart) { +// if (i >= greenStart && i < greenEnd) { +// ntft->fillTriangle(x0, y0, x1, y1, x2, y2, TFT_GREEN); +// ntft->fillTriangle(x1, y1, x2, y2, x3, y3, TFT_GREEN); +// } +// } +// +// // Short scale tick length +// if (i % 25 != 0) tl = 8; +// +// // Recalculate coords in case tick length changed +// x0 = x + sx * (100 + tl) + 120; +// y0 = y + sy * (100 + tl) + 140; +// x1 = x + sx * 100 + 120; +// y1 = y + sy * 100 + 140; +// +// // Draw tick +// ntft->drawLine(x0, y0, x1, y1, TFT_BLACK); +// +// // Check if labels should be drawn, with position tweaks +// if (i % 25 == 0) { +// // Calculate label positions +// x0 = x + sx * (100 + tl + 10) + 120; +// y0 = y + sy * (100 + tl + 10) + 140; +// switch (i / 25) { +// case -2: ntft->drawCentreString(ms0, x0, y0 - 12, 2); break; +// case -1: ntft->drawCentreString(ms1, x0, y0 - 9, 2); break; +// case 0: ntft->drawCentreString(ms2, x0, y0 - 6, 2); break; +// case 1: ntft->drawCentreString(ms3, x0, y0 - 9, 2); break; +// case 2: ntft->drawCentreString(ms4, x0, y0 - 12, 2); break; +// } +// } +// +// // Now draw the arc of the scale +// sx = cos((i + 5 - 90) * 0.0174532925); +// sy = sin((i + 5 - 90) * 0.0174532925); +// x0 = x + sx * 100 + 120; +// y0 = y + sy * 100 + 140; +// // Draw scale arc, don't draw the last part +// if (i < 50) ntft->drawLine(x0, y0, x1, y1, TFT_BLACK); +// } +// +// ntft->drawString(mlabel, x + 5 + 230 - 40, y + 119 - 20, 2); // Units at bottom right +// ntft->drawCentreString(mlabel, x + 120, y + 70, 4); // Comment out to avoid font 4 +// ntft->drawRect(x + 5, y + 3, 230, 119, TFT_BLACK); // Draw bezel line +// +// updateNeedle(0, 0); +//} +// +//// ######################################################################### +//// Update needle position +//// This function is blocking while needle moves, time depends on ms_delay +//// 10ms minimises needle flicker if text is drawn within needle sweep area +//// Smaller values OK if text not in sweep area, zero for instant movement but +//// does not look realistic... (note: 100 increments for full scale deflection) +//// ######################################################################### +//void MeterWidget::updateNeedle(float val, uint32_t ms_delay) +//{ +// int value = (val - scaleStart) * factor; +// +// ntft->setTextColor(TFT_BLACK, TFT_WHITE); +// char buf[8]; dtostrf(val, 5, 1, buf); +// +// ntft->drawRightString(buf, mx + 50, my + 119 - 20, 2); +// +// if (value < -10) value = -10; // Limit value to emulate needle end stops +// if (value > 110) value = 110; +// +// // Move the needle until new value reached +// while (value != old_analog) { +// if (old_analog < value) old_analog++; +// else old_analog--; +// +// if (ms_delay == 0) old_analog = value; // Update immediately id delay is 0 +// +// float sdeg = map(old_analog, -10, 110, -150, -30); // Map value to angle +// // Calculate tip of needle coords +// float sx = cos(sdeg * 0.0174532925); +// float sy = sin(sdeg * 0.0174532925); +// +// // Calculate x delta of needle start (does not start at pivot point) +// float tx = tan((sdeg + 90) * 0.0174532925); +// +// // Erase old needle image +// ntft->drawLine(mx + 120 + 20 * ltx - 1, my + 140 - 20, mx + osx - 1, my + osy, TFT_WHITE); +// ntft->drawLine(mx + 120 + 20 * ltx, my + 140 - 20, mx + osx, my + osy, TFT_WHITE); +// ntft->drawLine(mx + 120 + 20 * ltx + 1, my + 140 - 20, mx + osx + 1, my + osy, TFT_WHITE); +// +// // Re-plot text under needle +// ntft->setTextColor(TFT_BLACK); +// ntft->drawCentreString(mlabel, mx + 120, my + 70, 4); // // Comment out to avoid font 4 +// +// // Store new needle end coords for next erase +// ltx = tx; +// osx = sx * 98 + 120; +// osy = sy * 98 + 140; +// +// // Draw the needle in the new position, magenta makes needle a bit bolder +// // draws 3 lines to thicken needle +// ntft->drawLine(mx + 120 + 20 * ltx - 1, my + 140 - 20, mx + osx - 1, my + osy, TFT_RED); +// ntft->drawLine(mx + 120 + 20 * ltx, my + 140 - 20, mx + osx, my + osy, TFT_MAGENTA); +// ntft->drawLine(mx + 120 + 20 * ltx + 1, my + 140 - 20, mx + osx + 1, my + osy, TFT_RED); +// +// // Slow needle down slightly as it approaches new position +// if (abs(old_analog - value) < 10) ms_delay += ms_delay / 5; +// +// // Wait before next update +// delay(ms_delay); +// } +//} +// +//// ######################################################################### +//// Set red, orange, yellow and green start+end zones as a % of full scale +//// ######################################################################### +//void MeterWidget::setZones(uint16_t rs, uint16_t re, uint16_t os, uint16_t oe, uint16_t ys, uint16_t ye, uint16_t gs, uint16_t ge) +//{ +// // Meter scale is -50 to +50 +// redStart = rs - 50; +// redEnd = re - 50; +// orangeStart = os - 50; +// orangeEnd = oe - 50; +// yellowStart = ys - 50; +// yellowEnd = ye - 50; +// greenStart = gs - 50; +// greenEnd = ge - 50; +//} \ No newline at end of file diff --git a/src/widgets/clock/ClockWidget.h b/src/widgets/clock/ClockWidget.h new file mode 100644 index 0000000..69cbd94 --- /dev/null +++ b/src/widgets/clock/ClockWidget.h @@ -0,0 +1,83 @@ + +#ifndef _ClockWidget_h_ +#define _ClockWidget_h_ + +#include "Arduino.h" +#include "TFT_eSPI.h" + +// clock class +class ClockWidget +{ + public: + ClockWidget(TFT_eSPI* tft); + + //// Set red, orange, yellow and green start+end zones as a percentage of full scale + //void setZones(uint16_t rs, uint16_t re, uint16_t os, uint16_t oe, uint16_t ys, uint16_t ye, uint16_t gs, uint16_t ge); + //// Draw meter meter at x, y and define full scale range plus the scale labels + //void analogMeter(uint16_t x, uint16_t y, float fullScale, const char *units, const char *s0, const char *s1, const char *s2, const char *s3, const char *s4); + //// Draw meter meter at x, y and define full scale range plus the scale labels + //void analogMeter(uint16_t x, uint16_t y, float startScale, float endScale, const char *units, const char *s0, const char *s1, const char *s2, const char *s3, const char *s4); + //// Move needle to new position + //void updateNeedle(float value, uint32_t ms_delay); + + // Draw clock face at x, y, with radius, digit colour and face colour + void drawClockFace(uint32_t xPosition, uint32_t yPosition, uint32_t radius, uint32_t digitColour, uint32_t faceColour) + // Update the displayed time on the clock + void updateClock(uint32_t xPosition, uint32_t yPosition, uint32_t radius, uint32_t hourMinColour, uint32_t secondsColour, uint32_t faceColour) + + private: + // Pointer to TFT_eSPI class functions + TFT_eSPI* ctft; + + //float ltx; // x delta of needle start + //uint16_t osx, osy; // Saved x & y coords of needle end + //int old_analog; // Value last displayed + //int old_digital; // Value last displayed + + float sx, sy, mx, my, hx, hy; // computed hand x & y coords + float sdeg, mdeg, hdeg; // degrees per movement + uint16_t osx, osy, omx, omy, ohx, ohy; // saved H, M, S x & y coords for erase + uint16_t x0, x1, yy0, yy1; // clock face computations + uint32_t targetTime; // for next 1 second timeout + uint8_t hh, mm, ss; // time in hours, mins, seconds + bool initial; // initialisation flag + + // x, y coord for center of clock + uint32_t xPosition; + uint32_t yPosition; + uint32_t radius; + uint32_t digitColour; + uint32_t faceColour; + uint32_t hourMinColour; + uint32_t secondsColour; + + //// x, y coord of top left corner of meter graphic + //uint16_t mx; + //uint16_t my; + + //// Scale factor + //float factor; + //float scaleStart; + + //// Scale label + //char mlabel[9]; + + //// Scale values + //char ms0[5]; + //char ms1[5]; + //char ms2[5]; + //char ms3[5]; + //char ms4[5]; + + //// Scale colour zone start end end values + //int16_t redStart; + //int16_t redEnd; + //int16_t orangeStart; + //int16_t orangeEnd; + //int16_t yellowStart; + //int16_t yellowEnd; + //int16_t greenStart; + //int16_t greenEnd; +}; + +#endif