remove about edit spiffs try rtc
This commit is contained in:
480
data/about.html
480
data/about.html
@@ -1,480 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="de">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>Über NinjaCross Timer</title>
|
|
||||||
<style>
|
|
||||||
html {
|
|
||||||
overflow-x: hidden;
|
|
||||||
min-height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: "Arial", sans-serif;
|
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
||||||
min-height: 100vh;
|
|
||||||
width: 100vw;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
padding: 20px;
|
|
||||||
color: white;
|
|
||||||
position: relative;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.back-btn {
|
|
||||||
position: fixed;
|
|
||||||
top: 20px;
|
|
||||||
right: 20px;
|
|
||||||
background: rgba(255, 255, 255, 0.2);
|
|
||||||
border: 2px solid rgba(255, 255, 255, 0.3);
|
|
||||||
color: white;
|
|
||||||
padding: 12px 20px;
|
|
||||||
border-radius: 25px;
|
|
||||||
text-decoration: none;
|
|
||||||
font-size: 1rem;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
z-index: 1000;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-btn:hover {
|
|
||||||
background: rgba(255, 255, 255, 0.3);
|
|
||||||
border-color: rgba(255, 255, 255, 0.5);
|
|
||||||
transform: translateY(-2px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 3vh;
|
|
||||||
margin-top: 4vh;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header h1 {
|
|
||||||
font-size: clamp(2rem, 4vw, 3rem);
|
|
||||||
margin-bottom: 1vh;
|
|
||||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.header p {
|
|
||||||
font-size: clamp(1rem, 2vw, 1.3rem);
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-container {
|
|
||||||
width: 100%;
|
|
||||||
max-width: 1000px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 2rem;
|
|
||||||
padding: 0 2vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-card {
|
|
||||||
background: rgba(255, 255, 255, 0.1);
|
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
border-radius: 20px;
|
|
||||||
padding: clamp(20px, 3vh, 30px);
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
||||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
|
||||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-card:hover {
|
|
||||||
transform: translateY(-5px);
|
|
||||||
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-card h2 {
|
|
||||||
font-size: clamp(1.3rem, 2.5vw, 1.8rem);
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
color: #fff;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-card h3 {
|
|
||||||
font-size: clamp(1.1rem, 2vw, 1.4rem);
|
|
||||||
margin: 1.5rem 0 0.8rem 0;
|
|
||||||
color: #ffc107;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-card p {
|
|
||||||
font-size: clamp(0.9rem, 1.8vw, 1.1rem);
|
|
||||||
line-height: 1.6;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
opacity: 0.95;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-card ul {
|
|
||||||
margin: 1rem 0;
|
|
||||||
padding-left: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-card li {
|
|
||||||
font-size: clamp(0.9rem, 1.8vw, 1.1rem);
|
|
||||||
line-height: 1.6;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
opacity: 0.95;
|
|
||||||
}
|
|
||||||
|
|
||||||
.feature-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
||||||
gap: 1.5rem;
|
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.feature-item {
|
|
||||||
background: rgba(255, 255, 255, 0.1);
|
|
||||||
padding: 1.5rem;
|
|
||||||
border-radius: 15px;
|
|
||||||
text-align: center;
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
|
||||||
}
|
|
||||||
|
|
||||||
.feature-item h4 {
|
|
||||||
font-size: clamp(1rem, 2vw, 1.2rem);
|
|
||||||
margin-bottom: 0.8rem;
|
|
||||||
color: #3498db;
|
|
||||||
}
|
|
||||||
|
|
||||||
.feature-item p {
|
|
||||||
font-size: clamp(0.85rem, 1.6vw, 1rem);
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stats-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
|
||||||
gap: 1rem;
|
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-item {
|
|
||||||
background: rgba(46, 204, 113, 0.2);
|
|
||||||
border: 2px solid #2ecc71;
|
|
||||||
padding: 1.5rem;
|
|
||||||
border-radius: 15px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-number {
|
|
||||||
font-size: clamp(1.8rem, 3vw, 2.5rem);
|
|
||||||
font-weight: bold;
|
|
||||||
color: #2ecc71;
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-label {
|
|
||||||
font-size: clamp(0.8rem, 1.5vw, 1rem);
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.team-section {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.team-member {
|
|
||||||
background: rgba(155, 89, 182, 0.2);
|
|
||||||
border: 2px solid #9b59b6;
|
|
||||||
padding: 1.5rem;
|
|
||||||
border-radius: 15px;
|
|
||||||
margin: 1rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.team-member h4 {
|
|
||||||
font-size: clamp(1.1rem, 2vw, 1.4rem);
|
|
||||||
color: #9b59b6;
|
|
||||||
margin-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.team-member p {
|
|
||||||
font-size: clamp(0.9rem, 1.6vw, 1rem);
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cta-section {
|
|
||||||
background: rgba(52, 152, 219, 0.2);
|
|
||||||
border: 2px solid #3498db;
|
|
||||||
text-align: center;
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cta-section::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: -50%;
|
|
||||||
left: -50%;
|
|
||||||
width: 200%;
|
|
||||||
height: 200%;
|
|
||||||
background: linear-gradient(45deg, transparent, rgba(255, 255, 255, 0.1), transparent);
|
|
||||||
animation: shine 3s infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes shine {
|
|
||||||
0% { transform: translateX(-100%) translateY(-100%) rotate(45deg); }
|
|
||||||
50% { transform: translateX(100%) translateY(100%) rotate(45deg); }
|
|
||||||
100% { transform: translateX(-100%) translateY(-100%) rotate(45deg); }
|
|
||||||
}
|
|
||||||
|
|
||||||
.cta-content {
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cta-btn {
|
|
||||||
display: inline-block;
|
|
||||||
background: #3498db;
|
|
||||||
color: white;
|
|
||||||
padding: 12px 30px;
|
|
||||||
border-radius: 25px;
|
|
||||||
text-decoration: none;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-top: 1rem;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
font-size: clamp(0.9rem, 1.8vw, 1.1rem);
|
|
||||||
}
|
|
||||||
|
|
||||||
.cta-btn:hover {
|
|
||||||
background: #2980b9;
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 5px 15px rgba(52, 152, 219, 0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.logo {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
top: 10px;
|
|
||||||
left: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-btn {
|
|
||||||
top: 10px;
|
|
||||||
right: 10px;
|
|
||||||
padding: 8px 15px;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-container {
|
|
||||||
padding: 0 3vw;
|
|
||||||
gap: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.feature-grid {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
|
||||||
.logo {
|
|
||||||
width: 35px;
|
|
||||||
height: 35px;
|
|
||||||
top: 8px;
|
|
||||||
left: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.back-btn {
|
|
||||||
top: 8px;
|
|
||||||
right: 8px;
|
|
||||||
padding: 6px 12px;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-container {
|
|
||||||
padding: 0 2vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
padding: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<a href="/" class="back-btn">
|
|
||||||
← Zurück zum Timer
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<div class="header">
|
|
||||||
<h1>🏊♀️ Über NinjaCross Timer</h1>
|
|
||||||
<p>Der professionelle Zeitmesser für Ninjacross Wettkämpfe</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content-container">
|
|
||||||
<div class="content-card">
|
|
||||||
<h2>🎯 Was ist NinjaCross?</h2>
|
|
||||||
<p>
|
|
||||||
NinjaCross ist ein aufregender Wassersport, der Geschwindigkeit, Technik und Athletik kombiniert.
|
|
||||||
Teilnehmer durchqueren Schwimmbahnen mit verschiedenen Hindernissen und Herausforderungen,
|
|
||||||
wobei Zeit und Präzision entscheidend sind.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Unser Timer-System wurde speziell entwickelt, um professionelle Wettkämpfe zu unterstützen
|
|
||||||
und präzise Zeitmessungen für bis zu zwei Bahnen gleichzeitig zu ermöglichen.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content-card">
|
|
||||||
<h2>⚡ Funktionen</h2>
|
|
||||||
<div class="feature-grid">
|
|
||||||
<div class="feature-item">
|
|
||||||
<h4>🎲 Dual-Timer</h4>
|
|
||||||
<p>Gleichzeitige Zeitmessung für zwei Bahnen mit präziser Synchronisation</p>
|
|
||||||
</div>
|
|
||||||
<div class="feature-item">
|
|
||||||
<h4>📱 Responsive Design</h4>
|
|
||||||
<p>Optimiert für alle Geräte - Desktop, Tablet und Smartphone</p>
|
|
||||||
</div>
|
|
||||||
<div class="feature-item">
|
|
||||||
<h4>🏆 Bestzeiten</h4>
|
|
||||||
<p>Automatische Verfolgung und Anzeige der besten Tageszeiten</p>
|
|
||||||
</div>
|
|
||||||
<div class="feature-item">
|
|
||||||
<h4>📚 Lernmodus</h4>
|
|
||||||
<p>Interaktiver Modus für Training und Schulungszwecke</p>
|
|
||||||
</div>
|
|
||||||
<div class="feature-item">
|
|
||||||
<h4>⚙️ Einfache Bedienung</h4>
|
|
||||||
<p>Intuitive Benutzeroberfläche für schnelle und fehlerfreie Bedienung</p>
|
|
||||||
</div>
|
|
||||||
<div class="feature-item">
|
|
||||||
<h4>🔄 Live-Sync</h4>
|
|
||||||
<p>Echtzeitaktualisierung aller Timer-Daten über Backend-Integration</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content-card">
|
|
||||||
<h2>📊 Technische Spezifikationen</h2>
|
|
||||||
<div class="stats-grid">
|
|
||||||
<div class="stat-item">
|
|
||||||
<span class="stat-number">0.01s</span>
|
|
||||||
<span class="stat-label">Präzision</span>
|
|
||||||
</div>
|
|
||||||
<div class="stat-item">
|
|
||||||
<span class="stat-number">2</span>
|
|
||||||
<span class="stat-label">Bahnen</span>
|
|
||||||
</div>
|
|
||||||
<div class="stat-item">
|
|
||||||
<span class="stat-number">50ms</span>
|
|
||||||
<span class="stat-label">Update-Rate</span>
|
|
||||||
</div>
|
|
||||||
<div class="stat-item">
|
|
||||||
<span class="stat-number">100%</span>
|
|
||||||
<span class="stat-label">Responsive</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h3>🔧 Technologie-Stack</h3>
|
|
||||||
<ul>
|
|
||||||
<li><strong>Frontend:</strong> HTML5, CSS3, Vanilla JavaScript</li>
|
|
||||||
<li><strong>Design:</strong> Responsive Grid Layout, Glassmorphism</li>
|
|
||||||
<li><strong>Performance:</strong> Optimierte Render-Zyklen, Smooth Animations</li>
|
|
||||||
<li><strong>Kompatibilität:</strong> Alle modernen Browser, Mobile-First</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content-card team-section">
|
|
||||||
<h2>👥 Entwicklung</h2>
|
|
||||||
<div class="team-member">
|
|
||||||
<h4>🚀 Entwickelt mit ❤️ von Carsten Graf</h4>
|
|
||||||
<p>
|
|
||||||
Dieses Projekt wurde mit Leidenschaft für den NinjaCross-Sport entwickelt,
|
|
||||||
um Wettkämpfe professioneller und spannender zu gestalten.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content-card">
|
|
||||||
<h2>🎮 Bedienung</h2>
|
|
||||||
<h3>Grundfunktionen</h3>
|
|
||||||
<ul>
|
|
||||||
<li><strong>Timer starten:</strong> Automatische Synchronisation mit Backend-System</li>
|
|
||||||
<li><strong>Live-Anzeige:</strong> Echtzeitaktualisierung aller Zeiten und Status</li>
|
|
||||||
<li><strong>Bestzeiten:</strong> Automatische Speicherung der Tagesrekorde</li>
|
|
||||||
<li><strong>Lernmodus:</strong> Interaktive Anweisungen für neue Benutzer</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h3>Status-Anzeigen</h3>
|
|
||||||
<ul>
|
|
||||||
<li><strong>Bereit (Blau):</strong> Timer ist startbereit</li>
|
|
||||||
<li><strong>Läuft (Grün):</strong> Aktive Zeitmessung mit Pulsation</li>
|
|
||||||
<li><strong>Beendet (Rot):</strong> Zeitmessung abgeschlossen</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content-card cta-section">
|
|
||||||
<div class="cta-content">
|
|
||||||
<h2>🏁 Bereit für den Wettkampf?</h2>
|
|
||||||
<p>
|
|
||||||
Starten Sie jetzt mit dem professionellen NinjaCross Timer
|
|
||||||
und erleben Sie präzise Zeitmessung auf höchstem Niveau!
|
|
||||||
</p>
|
|
||||||
<a href="/" class="cta-btn">Timer starten 🚀</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// Smooth scroll animations
|
|
||||||
const observerOptions = {
|
|
||||||
threshold: 0.1,
|
|
||||||
rootMargin: '0px 0px -50px 0px'
|
|
||||||
};
|
|
||||||
|
|
||||||
const observer = new IntersectionObserver((entries) => {
|
|
||||||
entries.forEach(entry => {
|
|
||||||
if (entry.isIntersecting) {
|
|
||||||
entry.target.style.opacity = '1';
|
|
||||||
entry.target.style.transform = 'translateY(0)';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, observerOptions);
|
|
||||||
|
|
||||||
// Initialize animations
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
const cards = document.querySelectorAll('.content-card');
|
|
||||||
cards.forEach(card => {
|
|
||||||
card.style.opacity = '0';
|
|
||||||
card.style.transform = 'translateY(30px)';
|
|
||||||
card.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
|
|
||||||
observer.observe(card);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add click effects
|
|
||||||
document.querySelectorAll('.content-card').forEach(card => {
|
|
||||||
card.addEventListener('click', function() {
|
|
||||||
this.style.transform = 'translateY(-5px) scale(1.02)';
|
|
||||||
setTimeout(() => {
|
|
||||||
this.style.transform = 'translateY(-5px) scale(1)';
|
|
||||||
}, 150);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -29,8 +29,8 @@ html {
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
top: 20px;
|
top: 20px;
|
||||||
left: 20px;
|
left: 20px;
|
||||||
width: 200px;
|
width: auto;
|
||||||
height: 60px;
|
height: auto;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
||||||
|
|||||||
@@ -9,8 +9,7 @@
|
|||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<a href="/about" class="logo" title="Über NinjaCross Timer">
|
<img src="/pictures/logo.png" class="logo" alt="NinjaCross Logo" />
|
||||||
<img src="/pictures/logo.png" alt="NinjaCross Logo" />
|
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a href="/settings" class="settings-btn">⚙️</a>
|
<a href="/settings" class="settings-btn">⚙️</a>
|
||||||
@@ -203,6 +202,7 @@ ws.onmessage = (event) => {
|
|||||||
// Smooth update every 50ms
|
// Smooth update every 50ms
|
||||||
setInterval(updateDisplay, 50);
|
setInterval(updateDisplay, 50);
|
||||||
|
|
||||||
|
|
||||||
// Initial load
|
// Initial load
|
||||||
syncFromBackend();
|
syncFromBackend();
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
BIN
data/ota/firmware.bin
Normal file
BIN
data/ota/firmware.bin
Normal file
Binary file not shown.
@@ -31,6 +31,7 @@ lib_deps =
|
|||||||
esp32async/AsyncTCP@^3.4.2
|
esp32async/AsyncTCP@^3.4.2
|
||||||
mlesniew/PicoMQTT@^1.3.0
|
mlesniew/PicoMQTT@^1.3.0
|
||||||
miguelbalboa/MFRC522@^1.4.12
|
miguelbalboa/MFRC522@^1.4.12
|
||||||
|
adafruit/RTClib@^2.1.4
|
||||||
|
|
||||||
[env:wemos_d1_mini32_OTA]
|
[env:wemos_d1_mini32_OTA]
|
||||||
board = wemos_d1_mini32
|
board = wemos_d1_mini32
|
||||||
@@ -42,6 +43,7 @@ lib_deps =
|
|||||||
esp32async/AsyncTCP@^3.4.2
|
esp32async/AsyncTCP@^3.4.2
|
||||||
mlesniew/PicoMQTT@^1.3.0
|
mlesniew/PicoMQTT@^1.3.0
|
||||||
miguelbalboa/MFRC522@^1.4.12
|
miguelbalboa/MFRC522@^1.4.12
|
||||||
|
adafruit/RTClib@^2.1.4
|
||||||
upload_protocol = espota
|
upload_protocol = espota
|
||||||
upload_port = 192.168.1.94
|
upload_port = 192.168.1.94
|
||||||
|
|
||||||
@@ -60,6 +62,7 @@ lib_deps =
|
|||||||
esp32async/AsyncTCP@^3.4.2
|
esp32async/AsyncTCP@^3.4.2
|
||||||
mlesniew/PicoMQTT@^1.3.0
|
mlesniew/PicoMQTT@^1.3.0
|
||||||
miguelbalboa/MFRC522@^1.4.12
|
miguelbalboa/MFRC522@^1.4.12
|
||||||
|
adafruit/RTClib@^2.1.4
|
||||||
|
|
||||||
[env:esp32thing]
|
[env:esp32thing]
|
||||||
board = esp32thing
|
board = esp32thing
|
||||||
@@ -67,6 +70,8 @@ monitor_speed = 115200
|
|||||||
build_flags =
|
build_flags =
|
||||||
-DBOARD_HAS_PSRAM
|
-DBOARD_HAS_PSRAM
|
||||||
-mfix-esp32-psram-cache-issue
|
-mfix-esp32-psram-cache-issue
|
||||||
|
board_upload.flash_size = 16MB
|
||||||
|
board_build.partitions = default_16MB.csv
|
||||||
targets = uploadfs
|
targets = uploadfs
|
||||||
board_build.psram = disabled
|
board_build.psram = disabled
|
||||||
lib_deps =
|
lib_deps =
|
||||||
@@ -76,6 +81,4 @@ lib_deps =
|
|||||||
esp32async/AsyncTCP@^3.4.2
|
esp32async/AsyncTCP@^3.4.2
|
||||||
mlesniew/PicoMQTT@^1.3.0
|
mlesniew/PicoMQTT@^1.3.0
|
||||||
miguelbalboa/MFRC522@^1.4.12
|
miguelbalboa/MFRC522@^1.4.12
|
||||||
|
adafruit/RTClib@^2.1.4
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -54,14 +54,12 @@ void readButtonJSON(const char * topic, const char * payload) {
|
|||||||
// Extract values from JSON
|
// Extract values from JSON
|
||||||
int pressType = doc["type"] | 0;
|
int pressType = doc["type"] | 0;
|
||||||
const char* buttonId = doc["buttonmac"] | "unknown";
|
const char* buttonId = doc["buttonmac"] | "unknown";
|
||||||
const char* messageId = doc["messageId"] | "unknown";
|
|
||||||
uint64_t timestamp = doc["timestamp"] | 0;
|
uint64_t timestamp = doc["timestamp"] | 0;
|
||||||
|
|
||||||
// Print received data
|
// Print received data
|
||||||
Serial.printf("Button Press Received:\n");
|
Serial.printf("Button Press Received:\n");
|
||||||
Serial.printf(" Type: %d\n", pressType);
|
Serial.printf(" Type: %d\n", pressType);
|
||||||
Serial.printf(" Button MAC: %s\n", buttonId);
|
Serial.printf(" Button MAC: %s\n", buttonId);
|
||||||
Serial.printf(" Message ID: %s\n", messageId);
|
|
||||||
Serial.printf(" Timestamp: %llu\n", timestamp);
|
Serial.printf(" Timestamp: %llu\n", timestamp);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
#define LED_PIN 13
|
#define LED_PIN 13
|
||||||
|
|
||||||
|
|
||||||
// Status LED
|
// Status LED
|
||||||
unsigned long lastLedBlink = 0;
|
unsigned long lastLedBlink = 0;
|
||||||
bool ledState = false;
|
bool ledState = false;
|
||||||
|
|||||||
@@ -5,6 +5,10 @@
|
|||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
#include <Wire.h>
|
||||||
|
#include "RTClib.h"
|
||||||
|
|
||||||
|
RTC_PCF8523 rtc;
|
||||||
|
|
||||||
// Globale Zeitvariablen
|
// Globale Zeitvariablen
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
@@ -12,6 +16,9 @@ struct timezone tz;
|
|||||||
time_t now;
|
time_t now;
|
||||||
struct tm timeinfo;
|
struct tm timeinfo;
|
||||||
|
|
||||||
|
//Prototypen für Zeit-Management-Funktionen
|
||||||
|
void setupRTC();
|
||||||
|
void setRTC(DateTime dt);
|
||||||
void setupTimeAPI(AsyncWebServer& server);
|
void setupTimeAPI(AsyncWebServer& server);
|
||||||
String getCurrentTimeJSON();
|
String getCurrentTimeJSON();
|
||||||
bool setSystemTime(long timestamp);
|
bool setSystemTime(long timestamp);
|
||||||
@@ -49,6 +56,7 @@ bool setSystemTime(long timestamp) {
|
|||||||
|
|
||||||
if (settimeofday(&tv, NULL) == 0) {
|
if (settimeofday(&tv, NULL) == 0) {
|
||||||
Serial.println("Zeit erfolgreich gesetzt: " + String(timestamp));
|
Serial.println("Zeit erfolgreich gesetzt: " + String(timestamp));
|
||||||
|
setRTC(DateTime(timestamp));
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
Serial.println("Fehler beim Setzen der Zeit");
|
Serial.println("Fehler beim Setzen der Zeit");
|
||||||
@@ -58,6 +66,8 @@ bool setSystemTime(long timestamp) {
|
|||||||
|
|
||||||
void setupTimeAPI(AsyncWebServer& server) {
|
void setupTimeAPI(AsyncWebServer& server) {
|
||||||
|
|
||||||
|
setupRTC();
|
||||||
|
|
||||||
// API-Endpunkt: Aktuelle Zeit abrufen
|
// API-Endpunkt: Aktuelle Zeit abrufen
|
||||||
server.on("/api/time", HTTP_GET, [](AsyncWebServerRequest *request){
|
server.on("/api/time", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||||
String response = getCurrentTimeJSON();
|
String response = getCurrentTimeJSON();
|
||||||
@@ -202,3 +212,37 @@ uint64_t getCurrentTimestampMs() {
|
|||||||
gettimeofday(&tv, NULL);
|
gettimeofday(&tv, NULL);
|
||||||
return (uint64_t)tv.tv_sec * 1000LL + (uint64_t)tv.tv_usec / 1000LL;
|
return (uint64_t)tv.tv_sec * 1000LL + (uint64_t)tv.tv_usec / 1000LL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setupRTC() {
|
||||||
|
|
||||||
|
Wire.begin();
|
||||||
|
|
||||||
|
Serial.println("Initialisiere RTC...");
|
||||||
|
// Versuche RTC mit Wire zu initialisieren
|
||||||
|
if (!rtc.begin()) { // Versuche RTC zu initialisieren, Timeout nach 10 Sekunden
|
||||||
|
Serial.println("RTC nicht gefunden! Versuche erneut...");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rtc.initialized()) {
|
||||||
|
Serial.println("RTC nicht initialisiert, versuche Initialisierung...");
|
||||||
|
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
|
||||||
|
} else {
|
||||||
|
Serial.println("RTC bereits initialisiert.");
|
||||||
|
}
|
||||||
|
|
||||||
|
rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // Setze die RTC auf die Kompilierungszeit
|
||||||
|
Serial.println("RTC initialisiert.");
|
||||||
|
// Aktuelle Zeit vom RTC abrufen
|
||||||
|
DateTime now = rtc.now();
|
||||||
|
Serial.printf("Aktuelle RTC-Zeit: %04d-%02d-%02d %02d:%02d:%02d\n",
|
||||||
|
now.year(), now.month(), now.day(), now.hour(), now.minute(), now.second());
|
||||||
|
rtc.start(); // RTC starten, falls gestoppt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Funktion zum Setzen der RTC-Zeit
|
||||||
|
void setRTC(DateTime dt) {
|
||||||
|
rtc.adjust(dt);
|
||||||
|
DateTime newtime = rtc.now();
|
||||||
|
Serial.printf("RTC-Zeit gesetzt: %04d-%02d-%02d %02d:%02d:%02d\n",
|
||||||
|
newtime.year(), newtime.month(), newtime.day(), newtime.hour(), newtime.minute(), newtime.second());
|
||||||
|
}
|
||||||
@@ -19,11 +19,7 @@ void setupWifi() {
|
|||||||
|
|
||||||
uniqueSSID = getUniqueSSID();
|
uniqueSSID = getUniqueSSID();
|
||||||
ssidAP = uniqueSSID.c_str();
|
ssidAP = uniqueSSID.c_str();
|
||||||
//print station SSID
|
if (ssidSTA == nullptr || passwordSTA == nullptr || String(ssidSTA).isEmpty() || String(passwordSTA).isEmpty() ) {
|
||||||
Serial.println("Access Point SSID: " + String(ssidSTA));
|
|
||||||
Serial.println("Access Point PW: " + String(passwordSTA));
|
|
||||||
|
|
||||||
if (ssidSTA == nullptr || passwordSTA == nullptr || String(ssidSTA).isEmpty() || String(passwordSTA).isEmpty() ) {
|
|
||||||
Serial.println("Fehler: ssidSTA oder passwordSTA ist null!");
|
Serial.println("Fehler: ssidSTA oder passwordSTA ist null!");
|
||||||
WiFi.mode(WIFI_MODE_AP);
|
WiFi.mode(WIFI_MODE_AP);
|
||||||
WiFi.softAP(ssidAP, passwordAP);
|
WiFi.softAP(ssidAP, passwordAP);
|
||||||
|
|||||||
Reference in New Issue
Block a user