Skip to content

feat: Add MLX90614 infrared temperature sensor support#3107

Open
voldemort9999 wants to merge 2 commits intofossasia:flutterfrom
voldemort9999:feat/mlx90614-sensor
Open

feat: Add MLX90614 infrared temperature sensor support#3107
voldemort9999 wants to merge 2 commits intofossasia:flutterfrom
voldemort9999:feat/mlx90614-sensor

Conversation

@voldemort9999
Copy link

Summary

The MLX90614 infrared temperature sensor was listed in the sensor screen but tapping it showed "screen not implemented". This PR adds the full sensor implementation following the same architecture used by BMP180 and the other existing sensors.

What changed

The sensor communicates over I2C at address 0x5A using SMBus. It returns 3 bytes per read (LSB, MSB, PEC) and the temperature is derived using raw * 0.02 - 273.15 to convert from the sensor's internal Kelvin representation to Celsius. Two registers are used: 0x07 for object temperature and 0x06 for ambient temperature.

The provider handles periodic polling with a configurable time gap, buffers chart data points, and supports start/stop, looping, and clearing. The screen shows the current readings in data cards at the top, two live scrolling charts below, and the standard controls bar at the bottom.

Code changes

  • lib/communication/sensors/mlx90614.dart (new)
  • lib/providers/mlx90614_provider.dart (new)
  • lib/view/mlx90614_screen.dart (new)
  • lib/view/sensors_screen.dart — wired navigation for MLX90614
  • lib/l10n/app_en.arb — added mlx90614, objectTemperature, ambientTemperature
  • lib/theme/colors.dart — added mlx90614ChartColors

Tests

Added 65+ unit tests across two files covering communication and provider layers.

  • test/mlx90614_communication_test.dart
  • test/mlx90614_provider_test.dart

Communication tests verify I2C register reads, temperature conversion accuracy at reference points (-40°C, 0°C, 25°C, 37°C, 100°C, 300°C), error handling for short/missing data, PEC byte handling, and byte masking. Provider tests cover initial state, data collection lifecycle, configuration changes, data clearing, and list immutability.

flutter test test/mlx90614_communication_test.dart test/mlx90614_provider_test.dart

Notes

This follows the same pattern already used by BMP180, SHT21, and ADS1115 and keeps the change consistent with the rest of the codebase. An earlier attempt in #3025 was closed as it was missing charts and the controls bar.

Closes #2992

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @voldemort9999, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@voldemort9999 voldemort9999 force-pushed the feat/mlx90614-sensor branch 8 times, most recently from fd89f3a to 55dd20b Compare March 2, 2026 15:28
Implement complete MLX90614 sensor integration with the standard
three-layer architecture following the BMP180 reference pattern.

Communication layer (mlx90614.dart):
- Read object and ambient temperature over I2C at address 0x5A
- Parse 3-byte SMBus response (LSB, MSB, PEC)
- Convert raw value to Celsius (raw * 0.02 - 273.15)

Provider layer (mlx90614_provider.dart):
- Periodic data collection with configurable time gap
- Chart data buffering for live graphs
- Start/stop, looping, and clear controls

View layer (mlx90614_screen.dart):
- Raw data section showing object and ambient temperature
- Two live scrolling charts (object temp, ambient temp)
- Sensor controls bar (play/pause, loop, timegap, samples, clear)

Also:
- Add localization strings (mlx90614, objectTemperature, ambientTemperature)
- Add chart colors for MLX90614
- Wire MLX90614Screen in sensors_screen.dart navigation

Closes fossasia#2992
@github-actions
Copy link
Contributor

github-actions bot commented Mar 2, 2026

Build Status

Build successful. APKs to test: https://github.com/fossasia/pslab-app/actions/runs/22620928034/artifacts/5739542683.

Screenshots

Android Screenshots
iPhone Screenshots
iPad Screenshots

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds full MLX90614 IR temperature sensor support to the PSLab Flutter app, completing the previously unimplemented sensor entry by introducing communication, provider/state management, UI, navigation wiring, and tests.

Changes:

  • Added MLX90614 I2C/SMBus communication layer with temperature conversion and raw reads.
  • Added MLX90614 provider + dedicated screen with live charts and standard sensor controls.
  • Wired MLX90614 into the sensors list/navigation and added new l10n keys + chart colors; added unit tests for comms/provider.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
lib/communication/sensors/mlx90614.dart New MLX90614 sensor communication implementation over I2C/SMBus registers.
lib/providers/mlx90614_provider.dart New ChangeNotifier provider handling init, polling, buffering, looping, and clearing.
lib/view/mlx90614_screen.dart New UI screen displaying ambient/object readings, charts, and controls.
lib/view/sensors_screen.dart Adds navigation route to the new MLX90614 screen.
lib/l10n/app_en.arb Adds new localized strings for MLX90614 + temperature labels.
lib/theme/colors.dart Adds MLX90614 chart color palette.
test/mlx90614_communication_test.dart Unit tests for register reads, conversions, and error handling.
test/mlx90614_provider_test.dart Unit tests for provider state + lifecycle behaviors.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +666 to +669
"pin_res_desc": "Resistance Measurement Pin",
"mlx90614" : "MLX90614",
"objectTemperature" : "Object Temp",
"ambientTemperature" : "Ambient Temp"
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The added ARB keys are not reflected in the generated localization Dart files (e.g., lib/l10n/app_localizations.dart has no mlx90614/objectTemperature/ambientTemperature getters). Please rerun flutter gen-l10n (and format lib/l10n/) and commit the regenerated app_localizations*.dart outputs so builds/tests don’t fail at runtime/CI.

Suggested change
"pin_res_desc": "Resistance Measurement Pin",
"mlx90614" : "MLX90614",
"objectTemperature" : "Object Temp",
"ambientTemperature" : "Ambient Temp"
"pin_res_desc": "Resistance Measurement Pin"

Copilot uses AI. Check for mistakes.
Comment on lines +667 to +669
"mlx90614" : "MLX90614",
"objectTemperature" : "Object Temp",
"ambientTemperature" : "Ambient Temp"
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ARB formatting is inconsistent with the rest of the file ("key" : "value" has extra spaces before the colon). This can cause noisy diffs and may be reformatted by tooling; please match the existing JSON style ("key": "value").

Suggested change
"mlx90614" : "MLX90614",
"objectTemperature" : "Object Temp",
"ambientTemperature" : "Ambient Temp"
"mlx90614": "MLX90614",
"objectTemperature": "Object Temp",
"ambientTemperature": "Ambient Temp"

Copilot uses AI. Check for mistakes.

@override
void dispose() {
_provider.dispose();
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This State manually calls _provider.dispose(), but the ChangeNotifierProvider(create: ...) will also dispose the notifier automatically when the widget is removed. That can lead to a double-dispose assertion in debug builds. Remove the manual dispose call, or switch to ChangeNotifierProvider.value with explicit ownership semantics if you need manual disposal.

Suggested change
_provider.dispose();

Copilot uses AI. Check for mistakes.
Comment on lines +104 to +112
setUp(() {
getIt.reset();
final fakeCommunicationHandler = FakeCommunicationHandler();
getIt.registerLazySingleton<SocketClient>(() => SocketClient());
getIt.registerLazySingleton<ScienceLabCommon>(
() => ScienceLabCommon(fakeCommunicationHandler),
);
mockI2C = MockI2C();
});
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetIt.reset() is async in get_it v9; calling it without await can leave the container in an intermediate state while the test continues registering/reading singletons. Make setUp/tearDown async and await getIt.reset() to avoid test flakiness.

Copilot uses AI. Check for mistakes.
Comment on lines +13 to +22
setUp(() {
getIt.reset();
getIt.registerLazySingleton<AppLocalizations>(
() => lookupAppLocalizations(const Locale('en')),
);
provider = MLX90614Provider();
});

tearDown(() {
getIt.reset();
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetIt.reset() is async in get_it v9; calling it without await can leave the container in an intermediate state while the test continues. Make setUp/tearDown async and await getIt.reset() (and register after the awaited reset).

Suggested change
setUp(() {
getIt.reset();
getIt.registerLazySingleton<AppLocalizations>(
() => lookupAppLocalizations(const Locale('en')),
);
provider = MLX90614Provider();
});
tearDown(() {
getIt.reset();
setUp(() async {
await getIt.reset();
getIt.registerLazySingleton<AppLocalizations>(
() => lookupAppLocalizations(const Locale('en')),
);
provider = MLX90614Provider();
});
tearDown(() async {
await getIt.reset();

Copilot uses AI. Check for mistakes.

test('can be called multiple times without error', () {
provider.dispose();
// Second dispose should not throw
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test claims a “second dispose should not throw” but never actually calls dispose() the second time or asserts returnsNormally. As written it doesn’t validate the intended behavior; call provider.dispose() twice and wrap the second call in an expect(..., returnsNormally) (or doesNotThrow).

Suggested change
// Second dispose should not throw
// Second dispose should not throw
expect(() => provider.dispose(), returnsNormally);

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for sensor MLX90614

2 participants