Recent Posts

Pages: 1 2 [3] 4 5 ... 10
21
History / Re: Nikola Tesla Legacy
« Last post by MysteRy on October 27, 2025, 07:25:15 AM »

Seldom did I bring an umbrella, even though thunderstorms captivated my imagination.
22
History / Re: Nikola Tesla Legacy
« Last post by MysteRy on October 27, 2025, 07:24:03 AM »

Acknowledged nature as a source of inspiration while dismissing any explanations rooted in the supernatural.
23
History / Re: Nikola Tesla Legacy
« Last post by MysteRy on October 27, 2025, 07:22:26 AM »

In the 1919 series titled "My Inventions," the author delved into the fascinating realm of photographic memory techniques.
24
History / Re: Nikola Tesla Legacy
« Last post by MysteRy on October 27, 2025, 07:19:46 AM »

Correspondence envisioned a future free from the tangled mess of wires obstructing city vistas.
25
History / Re: Nikola Tesla Legacy
« Last post by MysteRy on October 27, 2025, 07:18:45 AM »

Delivered a talk at the Electrical Society exploring the potential of wireless communication over the ocean.
26
History / Re: Nikola Tesla Legacy
« Last post by MysteRy on October 27, 2025, 07:17:44 AM »

The IEEE Nikola Tesla Award celebrates exceptional advancements in the field of power electronics.
27
History / Re: Nikola Tesla Legacy
« Last post by MysteRy on October 27, 2025, 07:16:03 AM »

Released guidelines for constructing secure classroom coils utilizing neon sign transformers.
28
History / Re: Nikola Tesla Legacy
« Last post by MysteRy on October 27, 2025, 07:13:29 AM »

The patent for the Tesla valve was postponed until 1920, but it has since become a fundamental component in the field of microfluidics.
29
History / Re: Nikola Tesla Legacy
« Last post by MysteRy on October 27, 2025, 07:11:34 AM »

Sought the support of J.P. Morgan Jr. to breathe new life into the Wardenclyffe dream, but met with failure.
30
Token Generator Flutter App and ESP32






ESP32 Final Code
Code: [Select]
// =================================================================================
// FINAL CODE - Uses AudioTools (A2DP) + Bluetooth Serial (SPP) + MD_MAX72xx Display
// =================================================================================

//ESP32 Pin    PCM5100A Module Pin
//GPIO 26      BCK (BCLK)
//GPIO 25      I2S LRC (word select)
//GPIO 22      DIN (DATA)
//5V           VCC (Power input)
//GND          GND
//SPK+ / SPK-  Connect to 4Ω speaker (Left)
//SPK+ / SPK-  Connect to 4Ω speaker (Right)

#include <MD_MAX72xx.h>
#include "AudioTools.h"
#include "BluetoothA2DPSink.h"
#include "BluetoothSerial.h"

// --- Display Settings ---
#define DEBUG 1
#if DEBUG
#define PRINT(s, x)                 \
  {                                 \
    Serial.print(F(s));             \
    Serial.print(x);                \
  }
#define PRINTS(x) Serial.print(F(x))
#define PRINTD(x) Serial.println(x)
#else
#define PRINT(s, x)
#define PRINTS(x)
#define PRINTD(x)
#endif

#define HARDWARE_TYPE MD_MAX72XX::FC16_HW
#define MAX_DEVICES 8
#define CLK_PIN 18
#define DATA_PIN 23
#define CS_PIN 5
MD_MAX72XX mx = MD_MAX72XX(HARDWARE_TYPE, DATA_PIN, CLK_PIN, CS_PIN, MAX_DEVICES);

// --- Bluetooth Settings ---
I2SStream out;
BluetoothA2DPSink a2dp_sink(out);
BluetoothSerial SerialBT;

// ====================================================================
// DISPLAY FUNCTION
// ====================================================================
void displayText(const char *p)
{
  mx.clear();
  uint16_t logical_col = 0;

  while (*p != '\0')
  {
    uint8_t cBuf[8];
    uint8_t charWidth = mx.getChar(*p, sizeof(cBuf), cBuf);

    if (logical_col + charWidth > mx.getColumnCount())
    {
      break;
    }

    uint16_t physical_pos = (mx.getColumnCount() - 1) - logical_col;
    mx.setChar(physical_pos, *p);

    logical_col += charWidth + 1;
    p++;
  }
}

// ====================================================================
// SETUP FUNCTION
// ====================================================================
void setup()
{
#if DEBUG
  Serial.begin(115200);
#endif
  PRINTS("\n[MD_MAX72XX + BT Speaker/Display Demo]");

  // --- Initialize Display ---
  if (!mx.begin())
    PRINTS("\nMD_MAX72XX initialization failed");

  mx.control(MD_MAX72XX::INTENSITY, 5);
  mx.clear();

  displayText("WELCOME");
  delay(2000);
  mx.clear();

  // --- Configure I2S Audio Output ---
  PRINTS("\nConfiguring I2S...");
  auto cfg = out.defaultConfig();
  cfg.pin_bck = 26;
  cfg.pin_ws = 25;
  cfg.pin_data = 22;
  out.begin(cfg);
  PRINTS("\nI2S Configured.");

  // --- Initialize Bluetooth Services ---
  a2dp_sink.start("NOBLE-BT");
  PRINTS("\nBluetooth Audio 'NOBLE-BT' Initialized.");

  SerialBT.begin("NOBLE-DISPLAY");
  PRINTS("\nBluetooth Serial 'NOBLE-DISPLAY' Initialized. Ready for data.");
  displayText("READY");
}

// ====================================================================
// MAIN LOOP
// ====================================================================
void loop()
{
  if (SerialBT.available())
  {
    String receivedText = SerialBT.readStringUntil('\n');
    receivedText.trim();

    if (receivedText.length() > 0)
    {
      PRINTS("\nReceived: ");
      PRINTD(receivedText);
      displayText(receivedText.c_str());
    }
  }
}


Flutter Code
lib/token_generator_screen.dart
Code: [Select]
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:nms_billing/announcement_screen.dart';
import 'package:nms_billing/announcement_service.dart';
import 'package:nms_billing/firestore_service.dart';
import 'package:nms_billing/service_locator.dart';
import 'dart:async';
import 'package:nms_billing/bluetooth_service.dart';

class TokenGeneratorScreen extends StatefulWidget {
  const TokenGeneratorScreen({super.key});

  @override
  State<TokenGeneratorScreen> createState() => _TokenGeneratorScreenState();
}

class _TokenGeneratorScreenState extends State<TokenGeneratorScreen> {
  final _formKey = GlobalKey<FormState>();
  final _vehicleNoController = TextEditingController();
  final _contactNoController = TextEditingController();
  final _firestoreService = FirestoreService();
  final _announcementService = sl<AnnouncementService>();
  final _bluetoothService = BluetoothService();

  String? _generatedToken;
  bool _isLoading = true;
  bool _isBtConnected = false;
  bool _isConnecting = false;

  @override
  void initState() {
    super.initState();
    _loadPendingToken();
    Future.delayed(const Duration(seconds: 1), _connectToDisplay);
  }

  @override
  void dispose() {
    _bluetoothService.disconnect();
    _vehicleNoController.dispose();
    _contactNoController.dispose();
    super.dispose();
  }

  Future<void> _connectToDisplay() async {
    if (_isBtConnected || _isConnecting) return;
    setState(() => _isConnecting = true);
    bool success = await _bluetoothService.connect();
    if (mounted) {
      setState(() {
        _isBtConnected = success;
        _isConnecting = false;
      });
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text(success ? 'Connected to Display' : 'Failed to connect. Please pair device first.'),
          backgroundColor: success ? Colors.green : Colors.red,
          // --- CHANGE 1: Make SnackBar float ---
          behavior: SnackBarBehavior.floating,
        ),
      );
    }
  }

  Future<void> _loadPendingToken() async {
    setState(() { _isLoading = true; });
    try {
      String token = await _firestoreService.peekNextTokenNumber();
      if (mounted) {
        setState(() {
          _generatedToken = token;
          _isLoading = false;
        });
      }
    } catch (e) {
      print("Error loading pending token: $e");
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(SnackBar(
          content: Text('Error: $e'),
          // --- CHANGE 2: Make SnackBar float and add error color ---
          behavior: SnackBarBehavior.floating,
          backgroundColor: Colors.red,
        ));
        setState(() => _isLoading = false);
      }
    }
  }

  Future<void> _saveToken() async {
    if (_formKey.currentState!.validate()) {
      setState(() { _isLoading = true; });

      try {
        final vehicleNumber = 'KL-${_vehicleNoController.text.toUpperCase()}';
        final tokenData = {
          'tokenNumber': _generatedToken,
          'vehicleNo': vehicleNumber,
          'contactNo': _contactNoController.text,
          'timestamp': DateTime.now().toIso8601String(),
          'status': 0, // 0: Pending
          'paymentStatus': 'UNPAID',
        };

        await _firestoreService.saveToken(tokenData);
        await _firestoreService.incrementTokenCounter();

        _announcementService.addAnnouncement(
          token: _generatedToken!,
          vehicle: vehicleNumber,
        );

        if (mounted) {
          if (_isBtConnected) {
            final String tokenToDisplay = "NMS-TOK-${_generatedToken!}";
            await _bluetoothService.sendToken(tokenToDisplay);
            Navigator.of(context).pushReplacement(
              MaterialPageRoute(builder: (context) => const AnnouncementScreen()),
            );
          } else {
            ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
              content: Text('Token Generated Successfully!'),
              backgroundColor: Colors.green,
              // --- CHANGE 3: Make SnackBar float ---
              behavior: SnackBarBehavior.floating,
            ));

            _vehicleNoController.clear();
            _contactNoController.clear();
            await _loadPendingToken();
          }
        }
      } catch (e) {
        print("Error saving token: $e");
        if (mounted) {
          ScaffoldMessenger.of(context).showSnackBar(SnackBar(
            content: Text('Error saving token: $e'),
            // --- CHANGE 4: Make SnackBar float and add error color ---
            behavior: SnackBarBehavior.floating,
            backgroundColor: Colors.red,
          ));
          setState(() => _isLoading = false);
        }
      }
    }
  }

  // --- NO CHANGES NEEDED IN THE UI BUILDER METHODS ---
  @override
  Widget build(BuildContext context) {
    String currentDate = DateFormat('dd-MM-yyyy HH:mm').format(DateTime.now());

    return Scaffold(
      appBar: AppBar(title: const Text('Generate Token')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Form(
          key: _formKey,
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              _buildConnectionStatus(),
              const SizedBox(height: 16),
              Card(
                elevation: 4,
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    children: [
                      const Text('TOKEN NUMBER', style: TextStyle(fontSize: 18, color: Colors.grey)),
                      const SizedBox(height: 8),
                      if (_isLoading)
                        const CircularProgressIndicator()
                      else
                        Text(
                          _generatedToken ?? 'Error',
                          style: const TextStyle(fontSize: 40, fontWeight: FontWeight.bold, letterSpacing: 2),
                        ),
                      const SizedBox(height: 8),
                      Text(currentDate, style: const TextStyle(fontSize: 16)),
                    ],
                  ),
                ),
              ),
              const SizedBox(height: 24),
              TextFormField(
                controller: _vehicleNoController,
                textCapitalization: TextCapitalization.characters,
                decoration: const InputDecoration(
                  labelText: 'Vehicle No',
                  prefixText: 'KL-',
                  border: OutlineInputBorder(),
                ),
                validator: (v) => v!.isEmpty ? 'Enter vehicle number' : null,
              ),
              const SizedBox(height: 16),
              TextFormField(
                controller: _contactNoController,
                decoration: const InputDecoration(
                  labelText: 'Contact No',
                  border: OutlineInputBorder(),
                ),
                keyboardType: TextInputType.phone,
                validator: (v) => v!.isEmpty ? 'Enter contact number' : null,
              ),
              const Spacer(),
              ElevatedButton.icon(
                icon: const Icon(Icons.confirmation_number),
                label: const Text('GENERATE TOKEN'),
                onPressed: _isLoading ? null : _saveToken,
                style: ElevatedButton.styleFrom(
                  padding: const EdgeInsets.symmetric(vertical: 16),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildConnectionStatus() {
    return Container(
      padding: const EdgeInsets.all(12),
      decoration: BoxDecoration(
        color: _isBtConnected ? Colors.green.withOpacity(0.1) : Colors.red.withOpacity(0.1),
        borderRadius: BorderRadius.circular(8),
        border: Border.all(color: _isBtConnected ? Colors.green : Colors.red),
      ),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          Row(
            children: [
              Icon(_isBtConnected ? Icons.bluetooth_connected : Icons.bluetooth_disabled, color: _isBtConnected ? Colors.green : Colors.red),
              const SizedBox(width: 8),
              Text(
                _isBtConnected ? 'Display Connected' : 'Display Disconnected',
                style: const TextStyle(fontWeight: FontWeight.bold),
              ),
            ],
          ),
          if (_isConnecting)
            const SizedBox(height: 20, width: 20, child: CircularProgressIndicator(strokeWidth: 2))
          else if (!_isBtConnected)
            TextButton(onPressed: _connectToDisplay, child: const Text('CONNECT')),
        ],
      ),
    );
  }
}


Flutter Code
lib/anouncement_service.dart
Code: [Select]
import 'dart:async';

class AnnouncementData {
  final String tokenNumber;
  final String vehicleNumber;
  // --- CHANGE 1: Add the timestamp field ---
  final DateTime timestamp;

  AnnouncementData({
    required this.tokenNumber,
    required this.vehicleNumber,
    // --- CHANGE 2: Add the timestamp to the constructor ---
    required this.timestamp,
  });
}

class AnnouncementService {
  AnnouncementService._internal();
  static final AnnouncementService _instance = AnnouncementService._internal();
  factory AnnouncementService() {
    return _instance;
  }

  final List<AnnouncementData> _history = [];
  final _announcementController = StreamController<AnnouncementData>.broadcast();
  Stream<AnnouncementData> get announcementStream => _announcementController.stream;
  List<AnnouncementData> get history => List.unmodifiable(_history);

  void startListening() {
    // This method can be used in the future if you need to listen
    // to a real-time source like Firestore streams directly.
  }

  void addAnnouncement({required String token, required String vehicle}) {
    // --- CHANGE 3: When creating an announcement, include the current time ---
    final announcement = AnnouncementData(
      tokenNumber: token,
      vehicleNumber: vehicle,
      timestamp: DateTime.now(), // Record the current timestamp
    );
    _history.insert(0, announcement);
    _announcementController.add(announcement);
  }

  void clearHistory() {
    _history.clear();
  }

  void dispose() {
    _announcementController.close();
  }
}


Flutter Code
lib/anouncement_screen.dart
Code: [Select]
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_tts/flutter_tts.dart';
import 'package:intl/intl.dart';
import 'package:nms_billing/announcement_service.dart';
import 'package:nms_billing/firestore_service.dart';
import 'package:nms_billing/service_locator.dart';

enum TtsState { playing, stopped }

class AnnouncementScreen extends StatefulWidget {
  const AnnouncementScreen({super.key});

  @override
  State<AnnouncementScreen> createState() => _AnnouncementScreenState();
}

class _AnnouncementScreenState extends State<AnnouncementScreen> {
  late FlutterTts _flutterTts;
  StreamSubscription? _announcementSubscription;
  final AnnouncementService _announcementService = sl<AnnouncementService>();
  // --- CHANGE 2: Instantiate FirestoreService ---
  final FirestoreService _firestoreService = FirestoreService();

  // State Management
  final List<AnnouncementData> _recentAnnouncements = [];
  TtsState _ttsState = TtsState.stopped;
  String? _currentlyPlayingToken;
  String? _ttsError;
  String _currentLanguage = 'en-US';

  @override
  void initState() {
    super.initState();
    _flutterTts = FlutterTts();
    _loadLanguageAndInitialize();
    _listenForLiveUpdates();
    // The midnight timer logic is no longer needed as Firestore queries handle this.
  }


  Future<void> _loadLanguageAndInitialize() async {
    // The startup check is no longer needed here.
    _currentLanguage = await _firestoreService.getLanguage(); // Use Firestore
    await _initializeTts();
    _loadInitialDataAndPlay();
  }

  Future<void> _initializeTts() async {
    await _flutterTts.setSpeechRate(0.35);

    _flutterTts.setStartHandler(() {
      if (mounted) setState(() => _ttsState = TtsState.playing);
    });

    _flutterTts.setCompletionHandler(() {
      if (mounted) {
        setState(() {
          _ttsState = TtsState.stopped;
          _currentlyPlayingToken = null;
        });
      }
    });

    _flutterTts.setErrorHandler((msg) {
      if (mounted) setState(() => _ttsError = msg);
    });

    await _flutterTts.setLanguage(_currentLanguage);
  }

  void _loadInitialDataAndPlay() {
    // The logic here is simplified. The service's history
    // is now the source of truth for live data.
    if (mounted) {
      setState(() {
        _recentAnnouncements.clear();
        _recentAnnouncements.addAll(_announcementService.history);
      });
      if (_recentAnnouncements.isNotEmpty) {
        Future.delayed(const Duration(milliseconds: 400), () {
          _speak(_recentAnnouncements.first);
        });
      }
    }
  }

  void _listenForLiveUpdates() {
    _announcementSubscription = _announcementService.announcementStream.listen((newAnnouncement) {
      if (mounted) {
        setState(() {
          _recentAnnouncements.insert(0, newAnnouncement);
          _ttsError = null;
        });
        _speak(newAnnouncement);
      }
    });
  }

  // --- CHANGE 4: Use FirestoreService to manage language preference ---
  Future<void> _changeLanguage(String languageCode) async {
    await _stop();
    await _firestoreService.saveLanguage(languageCode); // Use Firestore

    var result = await _flutterTts.setLanguage(languageCode);
    if (result == 1) {
      if (mounted) {
        setState(() {
          _currentLanguage = languageCode;
          _ttsError = null;
        });
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text(languageCode == 'ml-IN' ? 'ഭാഷ മലയാളം ആയി സജ്ജീകരിച്ചു' : 'Language set to English'),
            duration: const Duration(seconds: 2),
          ),
        );
      }
    } else {
      if (mounted) setState(() => _ttsError = "Failed to set language.");
    }
  }

  // The _speak method's logic remains the same as it doesn't interact with storage.
  Future<void> _speak(AnnouncementData data) async {
    await _stop();
    await Future.delayed(const Duration(milliseconds: 100));
    if (mounted) setState(() => _currentlyPlayingToken = data.tokenNumber);

    await _flutterTts.setLanguage(_currentLanguage);

    String announcementText;
    if (_currentLanguage == 'ml-IN') {
      final tokenNumberSpoken = data.tokenNumber.replaceAll('NMS-', '');
      String vehicleToSpeak;
      final parts = data.vehicleNumber.split('-');
      if (parts.length > 1) {
        final vehiclePrefix = parts[0];
        final vehicleSuffix = parts.sublist(1).join('').toUpperCase();
        final RegExp segmentRegex = RegExp(r'([A-Z]+|\d+)');
        final matches = segmentRegex.allMatches(vehicleSuffix);
        final processedSegments = matches.map((match) {
          String segment = match.group(0)!;
          if (int.tryParse(segment) != null) {
            final RegExp pairRegex = RegExp(r'.{1,2}');
            final pairMatches = pairRegex.allMatches(segment);
            return pairMatches.map((m) => m.group(0)!).join(' ');
          } else {
            return segment;
          }
        });
        final suffixSpoken = processedSegments.join(' ');
        vehicleToSpeak = '$vehiclePrefix $suffixSpoken';
      } else {
        vehicleToSpeak = data.vehicleNumber;
      }
      announcementText = "ടോക്കൺ നമ്പർ $tokenNumberSpoken. വാഹനത്തിന്റെ നമ്പർ $vehicleToSpeak.";
    } else {
      String lastFourDigits = data.tokenNumber.length >= 4
          ? data.tokenNumber.substring(data.tokenNumber.length - 4)
          : data.tokenNumber;
      String tokenToSpeak = lastFourDigits.split('').join(' ');
      String vehicleToSpeak = data.vehicleNumber.replaceAll('-', ' ').toUpperCase().split('').join(' ');
      announcementText = "Token number, $tokenToSpeak. Vehicle number, $vehicleToSpeak.";
    }

    var result = await _flutterTts.speak(announcementText);
    if (result != 1) {
      if (mounted) {
        setState(() => _ttsError = "Speak command failed. Is language pack installed?");
      }
    }
  }

  Future<void> _stop() async {
    var result = await _flutterTts.stop();
    if (result == 1 && mounted) {
      setState(() {
        _ttsState = TtsState.stopped;
        _currentlyPlayingToken = null;
      });
    }
  }

  @override
  void dispose() {
    _announcementSubscription?.cancel();
    _flutterTts.stop();
    super.dispose();
  }

  // --- NO CHANGES NEEDED IN THE BUILD METHOD ---
  // The UI is driven by the state variables and is independent of the data source.
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Live Announcements'),
        actions: [
          PopupMenuButton<String>(
            onSelected: _changeLanguage,
            icon: const Icon(Icons.language),
            itemBuilder: (BuildContext context) => <PopupMenuEntry<String>>[
              const PopupMenuItem<String>(value: 'en-US', child: Text('English')),
              const PopupMenuItem<String>(value: 'ml-IN', child: Text('മലയാളം (Malayalam)')),
            ],
          ),
        ],
      ),
      body: Column(
        children: [
          if (_ttsError != null)
            Container(
              width: double.infinity,
              color: Colors.red[900],
              padding: const EdgeInsets.all(12.0),
              child: Text("TTS Error: $_ttsError", textAlign: TextAlign.center, style: const TextStyle(color: Colors.white)),
            ),
          Expanded(
            child: _recentAnnouncements.isEmpty
                ? const Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Icon(Icons.mic_off_outlined, size: 60, color: Colors.grey),
                  SizedBox(height: 16),
                  Text('No announcements yet for today.', style: TextStyle(fontSize: 18, color: Colors.grey)),
                ],
              ),
            )
                : ListView.builder(
              padding: const EdgeInsets.all(8.0),
              itemCount: _recentAnnouncements.length,
              itemBuilder: (context, index) {
                final announcement = _recentAnnouncements[index];
                return _buildAnnouncementCard(announcement);
              },
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildAnnouncementCard(AnnouncementData announcement) {
    final bool isPlaying = _ttsState == TtsState.playing && _currentlyPlayingToken == announcement.tokenNumber;
    return Card(
      elevation: 3,
      margin: const EdgeInsets.symmetric(vertical: 6, horizontal: 8),
      child: ListTile(
        leading: CircleAvatar(
          backgroundColor: isPlaying ? Colors.green : Theme.of(context).primaryColor,
          child: const Icon(Icons.campaign_outlined, color: Colors.white),
        ),
        title: Text(announcement.tokenNumber, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 18)),
        subtitle: Text('Vehicle: ${announcement.vehicleNumber}  •  ${DateFormat('hh:mm a').format(announcement.timestamp)}'),
        trailing: IconButton(
          iconSize: 30,
          icon: Icon(isPlaying ? Icons.stop_circle_outlined : Icons.play_circle_outline),
          color: isPlaying ? Colors.red : Theme.of(context).primaryColor,
          onPressed: () => isPlaying ? _stop() : _speak(announcement),
        ),
      ),
    );
  }
}

— Developed by Siva

Pages: 1 2 [3] 4 5 ... 10