Skip to content

Commit 003cd09

Browse files
authored
feat: implement SHT21 sensor support (#3258)
1 parent bc6835f commit 003cd09

11 files changed

Lines changed: 1206 additions & 340 deletions

assets/images/sht21.png

213 KB
Loading
Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,64 @@
11
import 'dart:async';
2-
import '../peripherals/i2c.dart';
2+
import 'package:pslab/communication/peripherals/i2c.dart';
3+
import 'package:pslab/others/logger_service.dart';
34

45
class SHT21 {
56
final I2C i2c;
67
static const int address = 0x40;
8+
static const String _tag = "SHT21";
79

8-
// Commands (Use Hold Master Mode for readBulk compatibility)
910
static const int tempHoldCmd = 0xE3;
1011
static const int humidityHoldCmd = 0xE5;
1112

1213
SHT21(this.i2c);
1314

15+
Future<bool> checkConnection() async {
16+
try {
17+
await i2c.writeBulk(address, [0xFE]);
18+
await Future.delayed(const Duration(milliseconds: 50));
19+
return true;
20+
} catch (e) {
21+
return false;
22+
}
23+
}
24+
1425
Future<double> getTemperature() async {
15-
// FIX: Use readBulk to handle the Write -> Restart -> Read sequence automatically.
16-
// This avoids the argument errors with .write() and .read()
1726
List<int> data = await i2c.readBulk(address, tempHoldCmd, 3);
27+
logger.d("$_tag RAW TEMP: $data");
1828

19-
if (data.length < 2) {
20-
throw Exception("Failed to read temperature from SHT21");
29+
if (data.length < 3 || !_verifyChecksum(data)) {
30+
throw Exception("Temp Checksum Failed");
2131
}
2232

23-
// Mask out status bits (last 2 bits) using & 0xFC
24-
int rawValue = (data[0] << 8) | (data[1] & 0xFC);
25-
26-
// Formula: T = -46.85 + 175.72 * (S_T / 2^16)
33+
int rawValue = ((data[0] & 0xFF) << 8) | (data[1] & 0xFC);
2734
return -46.85 + 175.72 * (rawValue / 65536.0);
2835
}
2936

3037
Future<double> getHumidity() async {
31-
// FIX: Use readBulk here too
3238
List<int> data = await i2c.readBulk(address, humidityHoldCmd, 3);
39+
logger.w("$_tag RAW HUMIDITY: $data");
3340

34-
if (data.length < 2) {
35-
throw Exception("Failed to read humidity from SHT21");
41+
if (data.length < 3 || !_verifyChecksum(data)) {
42+
throw Exception("Humidity Checksum Failed");
3643
}
3744

38-
// Mask out status bits
39-
int rawValue = (data[0] << 8) | (data[1] & 0xFC);
40-
41-
// Formula: RH = -6 + 125 * (S_RH / 2^16)
45+
int rawValue = ((data[0] & 0xFF) << 8) | (data[1] & 0xFC);
4246
return -6.0 + 125.0 * (rawValue / 65536.0);
4347
}
48+
49+
bool _verifyChecksum(List<int> data) {
50+
const int polynomial = 0x31;
51+
int crc = 0;
52+
for (int i = 0; i < 2; i++) {
53+
crc ^= data[i];
54+
for (int bit = 8; bit > 0; bit--) {
55+
if ((crc & 0x80) != 0) {
56+
crc = (crc << 1) ^ polynomial;
57+
} else {
58+
crc = crc << 1;
59+
}
60+
}
61+
}
62+
return (crc & 0xFF) == (data[2] & 0xFF);
63+
}
4464
}

lib/l10n/app_en.arb

Lines changed: 87 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -396,23 +396,23 @@
396396
"baroMeterBulletPoint2": "If you want to use the sensor BMP-180, connect the sensor to PSLab device as shown in the figure.",
397397
"baroMeterBulletPoint3": "The above pin configuration has to be same except for the pin GND. GND is meant for Ground and any of the PSLab device GND pins can be used since they are common.",
398398
"baroMeterBulletPoint4": "Select the sensor by going to the Configure tab from the bottom navigation bar and choose BMP-180 in the drop down menu under Select Sensor.",
399-
"magnetometerError" : "Magnetometer error:",
400-
"accelerometerError" : "Accelerometer error:",
399+
"magnetometerError": "Magnetometer error:",
400+
"accelerometerError": "Accelerometer error:",
401401
"compassTitle": "Compass",
402402
"parallelToGround": "Select axes parallel to ground",
403-
"thermometerTitle" : "Thermometer",
404-
"thermometerIntro" : "Thermometer instrument is used to measure ambient temprature. It can be measured using inbuilt ambient temprature sensor or through SHT21.",
403+
"thermometerTitle": "Thermometer",
404+
"thermometerIntro": "Thermometer instrument is used to measure ambient temprature. It can be measured using inbuilt ambient temprature sensor or through SHT21.",
405405
"celsius": "°C",
406-
"temperatureSensorError" : "Temperature sensor error:",
407-
"temperatureSensorInitialError" : "Temperature sensor initialization error:",
408-
"temperatureSensorUnavailableMessage" : "Ambient temperature sensor is not available on this device",
409-
"sharingMessage" : "Sharing PSLab Data",
410-
"delete" : "Delete",
406+
"temperatureSensorError": "Temperature sensor error:",
407+
"temperatureSensorInitialError": "Temperature sensor initialization error:",
408+
"temperatureSensorUnavailableMessage": "Ambient temperature sensor is not available on this device",
409+
"sharingMessage": "Sharing PSLab Data",
410+
"delete": "Delete",
411411
"deleteHint": "Are you sure you want to delete this file?",
412-
"soundmeterSnackBarMessage" : "Unable to access sound sensor",
413-
"dangerous" : "Dangerous",
414-
"documentationLink" : "https://docs.pslab.io/",
415-
"documentationError" : "Could not open the documentation link",
412+
"soundmeterSnackBarMessage": "Unable to access sound sensor",
413+
"dangerous": "Dangerous",
414+
"documentationLink": "https://docs.pslab.io/",
415+
"documentationError": "Could not open the documentation link",
416416
"androidRatingLink": "https://play.google.com/store/apps/details?id=io.pslab",
417417
"iOSRatingLink": "https://apps.apple.com/us/app/pslab/id6740454978?action=write-review",
418418
"ratingError": "Could not open the link",
@@ -453,66 +453,66 @@
453453
"accelerometerHighLimitHint": "Please provide the maximum limit of acceleration value to be recorded",
454454
"roboticArmIntro": "• A robotic arm is a programmable mechanical device that mimics the movement of a human arm.\n• It uses servo motors to control its motion, and these motors are operated using PWM signals.\n• The PSLab provides four PWM square wave generators (SQ1, SQ2, SQ3, SQ4), allowing control of up to four servo motors and enabling a robotic arm with up to four degrees of freedom.",
455455
"roboticArmConnection": "• In the above figure, SQ1 is connected to the signal pin of the first servo motor. The servo's GND pin is connected to both the PSLab’s GND and the external power supply GND, while the VCC pin is connected to the external power supply VCC.\n• Similarly, connect the remaining servos to SQ2, SQ3, and SQ4 along with their respective GND and power supply connections.\n• Once connected, each servo can be controlled using either circular sliders for manual control or a timeline-based sequence for automated movement.",
456-
"autoscan" : "Autoscan",
457-
"selectSensor" : "Select Sensor",
458-
"notConnected" : "Not Connected",
459-
"autoScanHint" : "Use Autoscan button to find connected sensors to PSLab device",
460-
"noSensorDetected" : "No sensors detected",
461-
"screenNotImplemented" : "screen not implemented yet",
462-
"timeGap" : "Time gap",
463-
"pslabNotConnected" : "PSLab not connected",
464-
"clearData" : "Clear Data",
465-
"numberOfSampes" : "No. of samples",
466-
"pressure" : "Pressure",
467-
"temperature" : "Temperature",
468-
"bmp180" : "BMP180",
469-
"plot" : "Plot",
470-
"dataCleared" : "Data cleared successfully",
471-
"rawData" : "Raw Data",
472-
"pressureUnitLabel" : "Pa",
473-
"temperatureUnitLabel" : "°C",
474-
"altitudeUnitLabel" : "m",
475-
"time" : "Time",
476-
"notAvailable" : "N/A",
477-
"estimated" : "Estimated",
478-
"data" : "Data",
479-
"configure" : "Configure",
480-
"setGain" : "Set Gain",
481-
"setChannel" : "Set Channel",
482-
"setRate" : "Set Rate",
483-
"millivolts" : "mV",
484-
"experiments" : "Experiments",
485-
"startExperiment" : "Start Experiment",
486-
"lightIntensityVsDistance" : "Light Intensity vs Distance",
487-
"lightIntensityVsDistanceDesc" : "Measure how light intensity changes with distance from the source",
488-
"stepCompleted" : "Step Completed!",
489-
"endExperiment" : "End Experiment",
490-
"next" : "Next",
491-
"previous" : "Previous",
492-
"step" : "Step",
493-
"experimentCompleted" : "Experiment Completed",
456+
"autoscan": "Autoscan",
457+
"selectSensor": "Select Sensor",
458+
"notConnected": "Not Connected",
459+
"autoScanHint": "Use Autoscan button to find connected sensors to PSLab device",
460+
"noSensorDetected": "No sensors detected",
461+
"screenNotImplemented": "screen not implemented yet",
462+
"timeGap": "Time gap",
463+
"pslabNotConnected": "PSLab not connected",
464+
"clearData": "Clear Data",
465+
"numberOfSampes": "No. of samples",
466+
"pressure": "Pressure",
467+
"temperature": "Temperature",
468+
"bmp180": "BMP180",
469+
"plot": "Plot",
470+
"dataCleared": "Data cleared successfully",
471+
"rawData": "Raw Data",
472+
"pressureUnitLabel": "Pa",
473+
"temperatureUnitLabel": "°C",
474+
"altitudeUnitLabel": "m",
475+
"time": "Time",
476+
"notAvailable": "N/A",
477+
"estimated": "Estimated",
478+
"data": "Data",
479+
"configure": "Configure",
480+
"setGain": "Set Gain",
481+
"setChannel": "Set Channel",
482+
"setRate": "Set Rate",
483+
"millivolts": "mV",
484+
"experiments": "Experiments",
485+
"startExperiment": "Start Experiment",
486+
"lightIntensityVsDistance": "Light Intensity vs Distance",
487+
"lightIntensityVsDistanceDesc": "Measure how light intensity changes with distance from the source",
488+
"stepCompleted": "Step Completed!",
489+
"endExperiment": "End Experiment",
490+
"next": "Next",
491+
"previous": "Previous",
492+
"step": "Step",
493+
"experimentCompleted": "Experiment Completed",
494494
"setUp": "Setup",
495-
"lightExperimentSetUpContent" : "Place your device near a light source (lamp, window, or flashlight).",
496-
"preparation" : "Preparation",
497-
"lightExperimentPreparationContent" : "Make sure you have space to move towards the light source gradually.",
498-
"instructions" : "Instructions",
499-
"lightExperimentInstructionContent" : "You will measure light intensity at different distances. Follow the on-screen prompts to move closer or farther from the light source.",
500-
"moveTowardsLight" : "Move towards the light source",
501-
"moveAwayFromLight" : "Move away from the light source",
502-
"holdPosition" : "Hold your position and let the reading stabilize",
503-
"followInstructions" : "Follow the on-screen instructions to set up your experiment.",
504-
"gesture" : "Gesture",
505-
"blueLabel" : "Blue",
506-
"greenLabel" : "Green",
507-
"proxLabel" : "Prox",
508-
"redLabel" : "Red",
509-
"mode" : "Mode",
510-
"configure" : "Configure",
495+
"lightExperimentSetUpContent": "Place your device near a light source (lamp, window, or flashlight).",
496+
"preparation": "Preparation",
497+
"lightExperimentPreparationContent": "Make sure you have space to move towards the light source gradually.",
498+
"instructions": "Instructions",
499+
"lightExperimentInstructionContent": "You will measure light intensity at different distances. Follow the on-screen prompts to move closer or farther from the light source.",
500+
"moveTowardsLight": "Move towards the light source",
501+
"moveAwayFromLight": "Move away from the light source",
502+
"holdPosition": "Hold your position and let the reading stabilize",
503+
"followInstructions": "Follow the on-screen instructions to set up your experiment.",
504+
"gesture": "Gesture",
505+
"blueLabel": "Blue",
506+
"greenLabel": "Green",
507+
"proxLabel": "Prox",
508+
"redLabel": "Red",
509+
"mode": "Mode",
510+
"configure": "Configure",
511511
"proximity": "Proximity",
512-
"light" : "Light",
513-
"lux" : "Lux",
514-
"distance" : "Distance",
515-
"distanceUnitLabel" : "mm",
512+
"light": "Light",
513+
"lux": "Lux",
514+
"distance": "Distance",
515+
"distanceUnitLabel": "mm",
516516
"legacyFirmwareAlertTitle": "Legacy Firmware Detected",
517517
"legacyFirmwareAlertMessage": "We have detected that your PSLab device is running legacy firmware. Please note that support for this firmware has ended. For the best experience and continued support, please update your device to the latest firmware version.",
518518
"holdPositionForPressure": "Hold position steady for stable pressure reading",
@@ -523,11 +523,11 @@
523523
"barometerExperimentSetUpContent": "Ensure your device has a working barometer sensor, or connect a BMP180 sensor using PSLab to complete the experiment.",
524524
"barometerExperimentPreparationContent": "Start the experiment in a stable position. Pressure decreases as altitude increases, and pressure increases as altitude decreases.",
525525
"barometerExperimentInstructionContent": "Follow the on-screen instructions to move between different altitudes. The experiment will automatically detect pressure changes.",
526-
"playbackStarted" : "Playback started",
527-
"playback" : "Playback",
528-
"stopPlayback" : "Stop Playback",
529-
"resumePlayback" : "Resume Playback",
530-
"pausePlayback" : "Pause Playback",
526+
"playbackStarted": "Playback started",
527+
"playback": "Playback",
528+
"stopPlayback": "Stop Playback",
529+
"resumePlayback": "Resume Playback",
530+
"pausePlayback": "Pause Playback",
531531
"openStreetMapContributors": "OpenStreetMap contributors",
532532
"location": "Location",
533533
"noLocationDataAvailable": "No location data available",
@@ -676,19 +676,22 @@
676676
"pin_cap_desc": "Capacitance Measurement Pin",
677677
"pin_res_name": "RES",
678678
"pin_res_desc": "Resistance Measurement Pin",
679-
"stopSound" : "Stop Sound",
680-
"saw" : "Saw",
679+
"stopSound": "Stop Sound",
680+
"saw": "Saw",
681681
"comingSoon": "Coming Soon",
682682
"startRecording": "Start Recording",
683683
"stopRecording": "Stop Recording",
684-
"tsl2561" : "TSL2561",
684+
"tsl2561": "TSL2561",
685685
"full": "Full Spectrum",
686686
"infrared": "Infrared",
687-
"infraredRaw" : "Infrared (raw)",
687+
"infraredRaw": "Infrared (raw)",
688688
"visibleLight": "Visible Light",
689-
"visibleLightRaw" : "Visible Light (raw)",
690-
"luxRaw" : "Luminosity (lux/raw)",
691-
"raw" : "raw",
689+
"visibleLightRaw": "Visible Light (raw)",
690+
"luxRaw": "Luminosity (lux/raw)",
691+
"raw": "raw",
692+
"thermometerConfig": "Thermometer Configurations",
693+
"fahrenheit": "Fahrenheit",
694+
"fahrenheitUnit": "°F",
692695
"pin_esp_name": "ESP",
693696
"pin_esp_desc": "ESP Programmer pin",
694697
"pin_vpl_name": "V+",
@@ -707,4 +710,4 @@
707710
"pin_dpl_desc": "Test pin for USB Data +",
708711
"pin_dmi_name": "D-",
709712
"pin_dmi_desc": "Test pin for USB Data -"
710-
}
713+
}

lib/models/thermometer_config.dart

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
class ThermometerConfig {
2+
final int updatePeriod;
3+
final String activeSensor;
4+
final String unit;
5+
final bool includeLocationData;
6+
7+
const ThermometerConfig({
8+
this.updatePeriod = 500,
9+
this.activeSensor = 'In-built Sensor',
10+
this.unit = 'Celsius',
11+
this.includeLocationData = false,
12+
});
13+
14+
ThermometerConfig copyWith({
15+
int? updatePeriod,
16+
String? activeSensor,
17+
String? unit,
18+
bool? includeLocationData,
19+
}) {
20+
return ThermometerConfig(
21+
updatePeriod: updatePeriod ?? this.updatePeriod,
22+
activeSensor: activeSensor ?? this.activeSensor,
23+
unit: unit ?? this.unit,
24+
includeLocationData: includeLocationData ?? this.includeLocationData,
25+
);
26+
}
27+
28+
Map<String, dynamic> toJson() {
29+
return {
30+
'updatePeriod': updatePeriod,
31+
'activeSensor': activeSensor,
32+
'unit': unit,
33+
'includeLocationData': includeLocationData,
34+
};
35+
}
36+
37+
factory ThermometerConfig.fromJson(Map<String, dynamic> json) {
38+
return ThermometerConfig(
39+
updatePeriod: json['updatePeriod'] as int? ?? 500,
40+
activeSensor: json['activeSensor'] as String? ?? 'In-built Sensor',
41+
unit: json['unit'] as String? ?? 'Celsius',
42+
includeLocationData: json['includeLocationData'] as bool? ?? false,
43+
);
44+
}
45+
}

0 commit comments

Comments
 (0)