Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions lib/communication/sensors/sht21.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import 'dart:async';
import '../peripherals/i2c.dart';

class SHT21 {
// SHT21 Default I2C Address
static const int addr = 0x40;

// The I2C helper instance (passed in from the main app)
final I2C i2c;

// Constructor: Ask for the I2C object instead of trying to create a new empty one
SHT21(this.i2c);

// Commands (No Hold Master Mode)
static const int _triggerTempMeasure = 0xF3;
static const int _triggerHumMeasure = 0xF5;

/// Read Temperature in Celsius
Future<double> getTemperature() async {
// 1. Send the "Measure" command
// We use writeBulk because it sends the bytes directly to the address
await i2c.writeBulk(addr, [_triggerTempMeasure]);

// 2. Wait for measurement (Datasheet max ~85ms)
await Future.delayed(Duration(milliseconds: 100));

// 3. Read 3 bytes (MSB, LSB, Checksum)
// simpleRead automatically handles the "Start Condition" + "Read" logic
List<int> data = await i2c.simpleRead(addr, 3);

if (data.length < 2) return 0.0;

// 4. Combine bytes & clear status bits
int rawValue = (data[0] << 8) | (data[1] & 0xFC);

// 5. Calculate Formula
return -46.85 + 175.72 * (rawValue / 65536.0);
}

/// Read Humidity in %RH
Future<double> getHumidity() async {
// 1. Send Measure Command
await i2c.writeBulk(addr, [_triggerHumMeasure]);

// 2. Wait
await Future.delayed(Duration(milliseconds: 100));

// 3. Read
List<int> data = await i2c.simpleRead(addr, 3);
if (data.length < 2) return 0.0;

int rawValue = (data[0] << 8) | (data[1] & 0xFC);

// 4. Calculate
return -6.0 + 125.0 * (rawValue / 65536.0);
}
}
6 changes: 6 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:pslab/providers/sht21_provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:provider/provider.dart';
Expand Down Expand Up @@ -42,6 +43,11 @@ void main() {
ChangeNotifierProvider<BoardStateProvider>(
create: (context) => getIt<BoardStateProvider>(),
),
// --- ADD THIS BLOCK BELOW ---
ChangeNotifierProvider<SHT21Provider>(
create: (context) => SHT21Provider(),
),
// ----------------------------
],
child: const MyApp(),
),
Expand Down
43 changes: 43 additions & 0 deletions lib/providers/sht21_provider.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import '../communication/sensors/sht21.dart';
import '../communication/peripherals/i2c.dart';

class SHT21Provider with ChangeNotifier {
SHT21? _sensor;
bool isWorking = false;
double temp = 0.0;
double hum = 0.0;

// Initialize the sensor with the I2C connection
Future<void> init(I2C i2c) async {
// This fixes the lint warning you saw earlier
_sensor ??= SHT21(i2c);
}

// Start the loop to read data
Future<void> startDataLog() async {
if (_sensor == null) return;

isWorking = true;
notifyListeners();

while (isWorking) {
// Fetch new values
temp = await _sensor!.getTemperature();
hum = await _sensor!.getHumidity();

// Update UI
notifyListeners();

// Wait 1 second before next read
await Future.delayed(Duration(milliseconds: 1000));
}
}

// Stop the loop
void stopDataLog() {
isWorking = false;
notifyListeners();
}
}
5 changes: 5 additions & 0 deletions lib/view/sensors_screen.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:pslab/view/sht21_screen.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:pslab/view/bmp180_screen.dart';
Expand Down Expand Up @@ -229,9 +230,13 @@ class _SensorsScreenState extends State<SensorsScreen> {
break;
case 'APDS9960':
targetScreen = const APDS9960Screen();
break;
case 'VL53L0X':
targetScreen = const VL53L0XScreen();
break;
case 'SHT21':
targetScreen = const SHT21Screen();
break;
default:
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
Expand Down
122 changes: 122 additions & 0 deletions lib/view/sht21_screen.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:pslab/communication/science_lab.dart';
import 'package:pslab/communication/peripherals/i2c.dart'; // Import I2C class
import 'package:pslab/providers/locator.dart';
import '../providers/sht21_provider.dart';

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

@override
State<SHT21Screen> createState() => _SHT21ScreenState();
}

class _SHT21ScreenState extends State<SHT21Screen> {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
final sht21Provider = Provider.of<SHT21Provider>(context, listen: false);

// 1. Get the ScienceLab instance
final scienceLab = getIt<ScienceLab>();

// 2. Check connection
if (scienceLab.isConnected()) {
// 3. MANUALLY create the I2C helper using the packet handler
// This fixes the "getter i2c not defined" error
I2C i2c = I2C(scienceLab.mPacketHandler);

// 4. Initialize the sensor provider
sht21Provider.init(i2c);
sht21Provider.startDataLog();
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Device not connected')),
);
}
});
}

@override
void dispose() {
// Stop the data loop when leaving the screen
if (mounted) {
Provider.of<SHT21Provider>(context, listen: false).stopDataLog();
}
super.dispose();
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('SHT21 Sensor'),
),
body: Consumer<SHT21Provider>(
builder: (context, provider, child) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
_buildSensorCard(
title: "Temperature",
value: provider.temp.toStringAsFixed(2),
unit: "°C",
icon: Icons.thermostat,
color: Colors.redAccent,
),
const SizedBox(height: 20),
_buildSensorCard(
title: "Humidity",
value: provider.hum.toStringAsFixed(2),
unit: "%",
icon: Icons.water_drop,
color: Colors.blueAccent,
),
],
),
);
},
),
);
}

Widget _buildSensorCard({
required String title,
required String value,
required String unit,
required IconData icon,
required Color color,
}) {
return Card(
elevation: 4,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Row(
children: [
Icon(icon, size: 40, color: color),
const SizedBox(width: 20),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title,
style: const TextStyle(fontSize: 18, color: Colors.grey)),
Row(
children: [
Text(value,
style: const TextStyle(
fontSize: 32, fontWeight: FontWeight.bold)),
const SizedBox(width: 5),
Text(unit, style: const TextStyle(fontSize: 20)),
],
),
],
),
],
),
),
);
}
}
Loading