Skip to content

feat: ported thermometer screen. #2761

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
Aug 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 4 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.BODY_SENSORS" />
<uses-feature android:name="android.hardware.usb.host" />
<uses-feature
android:name="android.hardware.sensor.ambient_temperature"
android:required="false" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
Expand Down
164 changes: 162 additions & 2 deletions android/app/src/main/java/io/pslab/MainActivity.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,166 @@
package io.pslab;

import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.util.Log;

import androidx.annotation.NonNull;

import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;

public class MainActivity extends FlutterActivity implements SensorEventListener {
private static final String TEMPERATURE_CHANNEL = "io.pslab/temperature";
private static final String TEMPERATURE_STREAM = "io.pslab/temperature_stream";
private static final String TAG = "MainActivity";
private SensorManager sensorManager;
private Sensor temperatureSensor;
private EventChannel.EventSink temperatureEventSink;
private boolean isListening = false;
private float currentTemperature = 0.0f;

@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);

sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
if (sensorManager != null) {
temperatureSensor = sensorManager.getDefaultSensor(Sensor.TYPE_AMBIENT_TEMPERATURE);
}

MethodChannel temperatureChannel = new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), TEMPERATURE_CHANNEL);
temperatureChannel.setMethodCallHandler(this::handleMethodCall);

EventChannel temperatureEventChannel = new EventChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), TEMPERATURE_STREAM);
temperatureEventChannel.setStreamHandler(new EventChannel.StreamHandler() {
@Override
public void onListen(Object arguments, EventChannel.EventSink events) {
temperatureEventSink = events;
startTemperatureUpdates();
}

@Override
public void onCancel(Object arguments) {
temperatureEventSink = null;
stopTemperatureUpdates();
}
});
}

private void handleMethodCall(MethodCall call, MethodChannel.Result result) {
switch (call.method) {
case "isTemperatureSensorAvailable":
result.success(temperatureSensor != null);
break;
case "getCurrentTemperature":
result.success((double) currentTemperature);
break;
case "startTemperatureUpdates":
if (startTemperatureUpdates()) {
result.success(true);
} else {
result.error("SENSOR_ERROR", "Failed to start temperature updates", null);
}
break;
case "stopTemperatureUpdates":
stopTemperatureUpdates();
result.success(true);
break;
default:
result.notImplemented();
break;
}
}

private boolean startTemperatureUpdates() {
if (temperatureSensor == null || sensorManager == null) {
Log.e(TAG, "Temperature sensor not available");
return false;
}

if (!isListening) {
boolean registered = sensorManager.registerListener(this, temperatureSensor, SensorManager.SENSOR_DELAY_NORMAL);
if (registered) {
isListening = true;
Log.d(TAG, "Temperature sensor listener registered");

if (currentTemperature != 0.0f && temperatureEventSink != null) {
Log.d(TAG, "Sending initial temperature to Flutter: " + currentTemperature);
temperatureEventSink.success((double) currentTemperature);
}

return true;
} else {
Log.e(TAG, "Failed to register temperature sensor listener");
return false;
}
}
return true;
}

private void stopTemperatureUpdates() {
if (isListening && sensorManager != null) {
sensorManager.unregisterListener(this, temperatureSensor);
isListening = false;
Log.d(TAG, "Temperature sensor listener unregistered");
}
}

@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_AMBIENT_TEMPERATURE) {
float temperature = event.values[0];

if (isValidTemperature(temperature)) {
currentTemperature = temperature;
Log.d(TAG, "Temperature updated: " + currentTemperature + "°C");

if (temperatureEventSink != null) {
Log.d(TAG, "Sending temperature to Flutter: " + currentTemperature);
temperatureEventSink.success((double) currentTemperature);
}
} else {
Log.w(TAG, "Invalid temperature reading: " + temperature + " - ignoring");
}
}
}

private boolean isValidTemperature(float temperature) {
if (Float.isNaN(temperature) || Float.isInfinite(temperature)) return false;
return temperature >= -273.15f && temperature <= 200f && Math.abs(temperature) <= 1e10f;
}

@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
Log.d(TAG, "Sensor accuracy changed: " + accuracy);
}

@Override
protected void onDestroy() {
super.onDestroy();
stopTemperatureUpdates();
}

@Override
protected void onPause() {
super.onPause();
if (isListening && sensorManager != null) {
sensorManager.unregisterListener(this);
}
}

public class MainActivity extends FlutterActivity {
}
@Override
protected void onResume() {
super.onResume();
if (isListening && temperatureSensor != null && sensorManager != null) {
sensorManager.registerListener(this, temperatureSensor, SensorManager.SENSOR_DELAY_NORMAL);
}
}
}
38 changes: 38 additions & 0 deletions lib/constants.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,43 @@
import 'dart:core';

List<String> instrumentHeadings = [
'OSCILLOSCOPE',
'MULTIMETER',
'LOGIC ANALYZER',
'SENSORS',
'WAVE GENERATOR',
'POWER SOURCE',
'LUX METER',
'ACCELEROMETER',
'BAROMETER',
'COMPASS',
'GYROSCOPE',
'THERMOMETER',
'ROBOTIC ARM',
'GAS SENSOR',
'DUST SENSOR',
'SOUND METER'
];

List<String> instrumentDesc = [
'Allows observation of varying signal voltages',
'Measure voltage, current, resistance and capacitance',
'Captures and displays signals from digital systems',
'Allows logging of data returned by sensor connected',
'Generates arbitrary analog and digital waveforms',
'Generates programmable voltage and currents',
'Measures the ambient light intensity',
'Measures the Linear acceleration in XYZ directions',
'Measures the atmospheric pressure',
'Three axes magnetometer pointing to magnetic north',
'Measures rate of rotation about XYZ axis',
'To measure the ambient temperature',
'Controls servos of a robotic arm',
'Air quality sensor for detecting a wide range of gases, including NH3, NOx, alcohol, benzene, smoke and CO2',
'Dust sensor is used to measure air quality in terms of particles per square meter',
'To measure the loudness in the environment in decibel(dB)'
];

List<String> instrumentIcons = [
'assets/icons/tile_icon_oscilloscope.png',
'assets/icons/tile_icon_multimeter.png',
Expand Down
10 changes: 8 additions & 2 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -323,8 +323,14 @@
"baroMeterBulletPoint2": "If you want to use the sensor BMP-180, connect the sensor to PSLab device as shown in the figure.",
"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.",
"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.",
"sharingMessage": "Sharing PSLab Data",
"delete": "Delete",
"thermometerTitle" : "Thermometer",
"thermometerIntro" : "Thermometer instrument is used to measure ambient temprature. It can be measured using inbuilt ambient temprature sensor or through SHT21.",
"celsius": "°C",
"temperatureSensorError" : "Temperature sensor error:",
"temperatureSensorInitialError" : "Temperature sensor initialization error:",
"temperatureSensorUnavailableMessage" : "Ambient temperature sensor is not available on this device",
"sharingMessage" : "Sharing PSLab Data",
"delete" : "Delete",
"deleteHint": "Are you sure you want to delete this file?",
"documentationLink" : "https://docs.pslab.io/",
"documentationError" : "Could not open the documentation link",
Expand Down
36 changes: 36 additions & 0 deletions lib/l10n/app_localizations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2032,6 +2032,42 @@ abstract class AppLocalizations {
/// **'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.'**
String get baroMeterBulletPoint4;

/// No description provided for @thermometerTitle.
///
/// In en, this message translates to:
/// **'Thermometer'**
String get thermometerTitle;

/// No description provided for @thermometerIntro.
///
/// In en, this message translates to:
/// **'Thermometer instrument is used to measure ambient temprature. It can be measured using inbuilt ambient temprature sensor or through SHT21.'**
String get thermometerIntro;

/// No description provided for @celsius.
///
/// In en, this message translates to:
/// **'°C'**
String get celsius;

/// No description provided for @temperatureSensorError.
///
/// In en, this message translates to:
/// **'Temperature sensor error:'**
String get temperatureSensorError;

/// No description provided for @temperatureSensorInitialError.
///
/// In en, this message translates to:
/// **'Temperature sensor initialization error:'**
String get temperatureSensorInitialError;

/// No description provided for @temperatureSensorUnavailableMessage.
///
/// In en, this message translates to:
/// **'Ambient temperature sensor is not available on this device'**
String get temperatureSensorUnavailableMessage;

/// No description provided for @sharingMessage.
///
/// In en, this message translates to:
Expand Down
20 changes: 20 additions & 0 deletions lib/l10n/app_localizations_en.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1038,6 +1038,26 @@ class AppLocalizationsEn extends AppLocalizations {
'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.';

@override
String get thermometerTitle => 'Thermometer';

@override
String get thermometerIntro =>
'Thermometer instrument is used to measure ambient temprature. It can be measured using inbuilt ambient temprature sensor or through SHT21.';

@override
String get celsius => '°C';

@override
String get temperatureSensorError => 'Temperature sensor error:';

@override
String get temperatureSensorInitialError =>
'Temperature sensor initialization error:';

@override
String get temperatureSensorUnavailableMessage =>
'Ambient temperature sensor is not available on this device';

String get sharingMessage => 'Sharing PSLab Data';

@override
Expand Down
2 changes: 2 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import 'package:pslab/view/about_us_screen.dart';
import 'package:pslab/view/software_licenses_screen.dart';
import 'package:pslab/theme/app_theme.dart';
import 'package:pslab/view/soundmeter_screen.dart';
import 'package:pslab/view/thermometer_screen.dart';
import 'package:pslab/view/wave_generator_screen.dart';
import 'constants.dart';

Expand Down Expand Up @@ -76,6 +77,7 @@ class MyApp extends StatelessWidget {
'/luxmeter': (context) => const LuxMeterScreen(),
'/barometer': (context) => const BarometerScreen(),
'/soundmeter': (context) => const SoundMeterScreen(),
'/thermometer': (context) => const ThermometerScreen(),
'/sensors': (context) => const SensorsScreen()
},
);
Expand Down
Loading
Loading