diff --git a/lib/main.dart b/lib/main.dart index 6a7ac6458..be0aa94d2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -14,6 +14,7 @@ import 'package:pslab/view/luxmeter_screen.dart'; import 'package:pslab/view/multimeter_screen.dart'; import 'package:pslab/view/oscilloscope_screen.dart'; import 'package:pslab/view/robotic_arm_screen.dart'; +import 'package:pslab/view/sensors_screen.dart'; import 'package:pslab/view/settings_screen.dart'; import 'package:pslab/view/about_us_screen.dart'; import 'package:pslab/view/software_licenses_screen.dart'; @@ -71,6 +72,7 @@ class MyApp extends StatelessWidget { '/luxmeter': (context) => const LuxMeterScreen(), '/barometer': (context) => const BarometerScreen(), '/soundmeter': (context) => const SoundMeterScreen(), + '/sensors': (context) => const SensorsScreen() }, ); } diff --git a/lib/view/instruments_screen.dart b/lib/view/instruments_screen.dart index 57d5b7b66..4af45e9d1 100644 --- a/lib/view/instruments_screen.dart +++ b/lib/view/instruments_screen.dart @@ -57,6 +57,18 @@ class _InstrumentsScreenState extends State { ); } break; + case 3: + if (Navigator.canPop(context) && + ModalRoute.of(context)?.settings.name == '/sensors') { + Navigator.popUntil(context, ModalRoute.withName('/sensors')); + } else { + Navigator.pushNamedAndRemoveUntil( + context, + '/sensors', + (route) => route.isFirst, + ); + } + break; case 6: if (Navigator.canPop(context) && ModalRoute.of(context)?.settings.name == '/luxmeter') { diff --git a/lib/view/sensors_screen.dart b/lib/view/sensors_screen.dart new file mode 100644 index 000000000..e6843f072 --- /dev/null +++ b/lib/view/sensors_screen.dart @@ -0,0 +1,219 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:pslab/view/widgets/common_scaffold_widget.dart'; +import '../../providers/board_state_provider.dart'; +import '../theme/colors.dart'; + +class SensorsScreen extends StatefulWidget { + const SensorsScreen({super.key}); + + @override + State createState() => _SensorsScreenState(); +} + +class _SensorsScreenState extends State { + bool _hasScanned = false; + List _detectedSensors = []; + Map _sensorAddresses = {}; + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, boardProvider, child) { + return CommonScaffold( + title: 'Sensors', + onGuidePressed: () { + // TODO + }, + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + SizedBox( + width: double.infinity, + height: 50, + child: ElevatedButton( + onPressed: () { + _performAutoscan(boardProvider); + }, + style: ElevatedButton.styleFrom( + backgroundColor: primaryRed, + foregroundColor: Colors.white, + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(4), + ), + ), + child: const Text( + 'AUTOSCAN', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), + ), + ), + const SizedBox(height: 20), + Container( + width: double.infinity, + padding: + const EdgeInsets.symmetric(vertical: 16, horizontal: 24), + decoration: BoxDecoration( + color: Colors.grey[100], + borderRadius: BorderRadius.circular(25), + border: Border.all( + color: Colors.grey[300]!, + width: 1, + ), + ), + child: Text( + _getStatusText(boardProvider), + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 16, + color: Colors.black87, + ), + ), + ), + if (_hasScanned) ...[ + const SizedBox(height: 30), + const Text( + 'SELECT SENSOR', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + color: Colors.black87, + ), + ), + const SizedBox(height: 20), + Expanded( + child: _buildSensorList(), + ), + ], + ], + ), + ), + ); + }, + ); + } + + String _getStatusText(BoardStateProvider boardProvider) { + if (!boardProvider.pslabIsConnected) { + return 'Not Connected'; + } + + if (!_hasScanned) { + return 'Use Autoscan button to find connected sensors to PSLab device'; + } + + if (_detectedSensors.isEmpty) { + return 'No sensors detected'; + } + + String result = ''; + for (String sensor in _detectedSensors) { + String address = _sensorAddresses[sensor] ?? ''; + result += '$address: [$sensor]\n'; + } + return result.trim(); + } + + void _performAutoscan(BoardStateProvider boardProvider) { + setState(() { + _hasScanned = true; + + if (boardProvider.pslabIsConnected) { + _detectedSensors = [ + 'HMC5883L', + 'VL53L0X', + 'TSL2561', + 'APDS9960', + 'SHT21', + 'ADS1115', + 'MLX90614', + 'CCS811', + 'MPU6050', + 'MPU925X', + 'BMP180', + ]; + _sensorAddresses = { + 'HMC5883L': '30', + 'VL53L0X': '41', + 'TSL2561': '57', + 'APDS9960': '57', + 'SHT21': '64', + 'ADS1115': '72', + 'MLX90614': '90', + 'CCS811': '90', + 'MPU6050': '104', + 'MPU925X': '105', + 'BMP180': '119', + }; + } else { + _detectedSensors = []; + _sensorAddresses = {}; + } + }); + } + + Widget _buildSensorList() { + final sensors = [ + 'ADS1115', + 'APDS9960', + 'BMP180', + 'CCS811', + 'HMC5883L', + 'MLX90614', + 'MPU6050', + 'MPU925X', + 'SHT21', + 'TSL2561', + 'VL53L0X', + ]; + + return ListView.builder( + itemCount: sensors.length, + itemBuilder: (context, index) { + final sensor = sensors[index]; + final isDetected = _detectedSensors.contains(sensor); + + return Container( + margin: const EdgeInsets.only(bottom: 1), + child: Material( + color: primaryRed, + child: InkWell( + onTap: () { + _onSensorTap(sensor); + }, + child: Container( + width: double.infinity, + padding: + const EdgeInsets.symmetric(vertical: 18, horizontal: 16), + decoration: BoxDecoration( + color: primaryRed, + border: isDetected + ? Border.all(color: Colors.white, width: 2) + : null, + ), + child: Text( + sensor, + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 16, + fontWeight: isDetected ? FontWeight.w600 : FontWeight.w500, + ), + ), + ), + ), + ), + ); + }, + ); + } + + void _onSensorTap(String sensorName) { + // TODO + } +}