Skip to content
Open
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
Binary file added docs/images/dust_sensor_sds011.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
81 changes: 81 additions & 0 deletions lib/communication/peripherals/uart.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import 'dart:typed_data';

import 'package:pslab/communication/commands_proto.dart';
import 'package:pslab/communication/packet_handler.dart';

class Uart2 {
static const double _clockRate = 64e6;

final PacketHandler packetHandler;
final bool requiresWriteAcknowledgement;
final CommandsProto _commands = CommandsProto();

Uart2(this.packetHandler, {this.requiresWriteAcknowledgement = false});

Future<void> configure(int baudRate) async {
if (baudRate <= 0) {
throw ArgumentError.value(baudRate, 'baudRate', 'Must be positive');
}
if (PacketHandler.boardType == BoardType.scpi) {
throw UnsupportedError('UART2 is not available on this PSLab model');
}

final divider = ((_clockRate / baudRate) / 4 - 1).round();
if (divider < 0 || divider > 0xffff) {
throw ArgumentError.value(
baudRate,
'baudRate',
'Cannot be generated by the UART2 clock',
);
}

packetHandler.sendByte(_commands.uart2);
packetHandler.sendByte(_commands.setBaud);
packetHandler.sendInt(divider);
final acknowledgement = await packetHandler.getAcknowledgement();
if (acknowledgement != 1) {
throw StateError(
'UART2 rejected baud-rate configuration (ACK $acknowledgement)',
);
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

Future<bool> hasData() async {
packetHandler.sendByte(_commands.uart2);
packetHandler.sendByte(_commands.readUart2Status);
return await _readResponseByte() != 0;
}

Future<int> readByte() async {
packetHandler.sendByte(_commands.uart2);
packetHandler.sendByte(_commands.readByte);
return _readResponseByte();
}

Future<void> writeByte(int value) async {
if (value < 0 || value > 0xff) {
throw ArgumentError.value(value, 'value', 'Must fit in one byte');
}
packetHandler.sendByte(_commands.uart2);
packetHandler.sendByte(_commands.sendByte);
packetHandler.sendByte(value);
if (requiresWriteAcknowledgement) {
await packetHandler.getAcknowledgement();
}
}

Future<void> write(List<int> values) async {
for (final value in values) {
await writeByte(value);
}
}

Future<int> _readResponseByte() async {
final response = Uint8List(1);
final bytesRead = await packetHandler.read(response, 1);
if (bytesRead != 1) {
throw StateError('UART2 response timed out');
}
return response[0];
}
}
2 changes: 2 additions & 0 deletions lib/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ List<String> instrumentIcons = [
'assets/icons/thermometer_logo.png',
'assets/icons/robotic_arm.png',
'assets/icons/tile_icon_gas.png', // Gas Sensor
'assets/icons/tile_icon_sensors.png', // Dust Sensor
'assets/icons/tile_icon_gas.png', // Sound Meter
'assets/icons/tile_icon_oled_screen.png',
];
Expand All @@ -48,6 +49,7 @@ List<String> instrumentNames = [
appLocalizations.thermometer.toLowerCase(),
appLocalizations.roboticArmTitle.toLowerCase(),
appLocalizations.gasSensor.toLowerCase(),
appLocalizations.dustSensor.toLowerCase(),
appLocalizations.soundMeter.toLowerCase(),
appLocalizations.oledDisplayTitle.toLowerCase(),
];
21 changes: 19 additions & 2 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,24 @@
"thermometerDesc": "To measure the ambient temperature",
"roboticArmDesc": "Controls servos of a robotic arm",
"gasSensorDesc": "Air quality sensor for detecting a wide range of gases",
"dustSensorDesc": "Dust sensor is used to measure air quality in terms of particles per square meter",
"dustSensorDesc": "Measures PM2.5 and PM10 with an SDS011 dust sensor",
"dustSensorPm25Axis": "PM2.5 (µg/m³)",
"dustSensorPm10": "PM10",
"dustSensorNotConnected": "Connect a PSLab V5 or V6 to start measuring.",
"dustSensorReadFailed": "No valid SDS011 data was received. Check the UART wiring and sensor power.",
"dustSensorReadingUart": "Reading SDS011 over UART",
"dustSensorStopped": "Measurement stopped",
"dustSensorStart": "START",
"dustSensorReset": "RESET",
"dustSensorGuideIntro": "This instrument reads PM2.5 and PM10 particulate-matter measurements from a Nova Fitness SDS011 over PSLab UART2.",
"dustSensorGuideMeasurement": "Values are reported by the SDS011 in micrograms per cubic metre (µg/m³). They are sensor measurements, not an air-quality index or a substitute for a calibrated reference instrument.",
"dustSensorGuideWiringTitle": "SDS011 UART wiring",
"dustSensorGuideWiringVcc": "Connect the sensor VCC pin to 5 V on PSLab.",
"dustSensorGuideWiringGnd": "Connect the sensor GND pin to any PSLab GND pin.",
"dustSensorGuideWiringSignal": "Cross the serial lines: connect SDS011 TXD to PSLab RXD and SDS011 RXD to PSLab TXD.",
"dustSensorGuideCalibration": "The SDS011 reports PM2.5 and PM10 directly. For quantitative experiments, compare it with a trusted reference and account for humidity, airflow, warm-up time, and sensor-to-sensor variation.",
"dustSensorGuideTroubleshooting": "If no data appears, verify the shared ground, 5 V supply, crossed TXD/RXD wiring, 9600 baud connection, and allow the sensor fan to stabilize.",
"dustSensorCompatibility": "SDS011 UART requires a PSLab model and firmware with working UART2 support. Phone-only use is not supported.",
"soundMeterDesc": "To measure the loudness in the environment in decibel(dB)",
"yAxisRange16V": "+/-16V",
"yAxisRange8V": "+/-8V",
Expand Down Expand Up @@ -969,4 +986,4 @@
"hardwareLimit" : "Hardware Limitation",
"hardwareLimitMessage" : "Generating analog and digital waveforms simultaneously on PSLab V5/V6 may cause distortion due to shared hardware pins (SI1/SQ4, SI2/SQ3).",
"pslabMini" : "PSLab Mini"
}
}
3 changes: 3 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import 'package:pslab/view/connect_device_screen.dart';
import 'package:pslab/view/faq_screen.dart';
import 'package:pslab/view/firmware_flasher_screen.dart';
import 'package:pslab/view/gas_sensor_screen.dart';
import 'package:pslab/view/dust_sensor_screen.dart';
import 'package:pslab/view/gyroscope_screen.dart';
import 'package:pslab/view/instruments_screen.dart';
import 'package:pslab/view/logged_data_screen.dart';
Expand Down Expand Up @@ -153,6 +154,8 @@ class MyApp extends StatelessWidget {
const _LocaleAware(child: ThermometerScreen()),
'/gassensor': (context) =>
const _LocaleAware(child: GasSensorScreen()),
'/dustSensor': (context) =>
const _LocaleAware(child: DustSensorScreen()),
'/sensors': (context) =>
const _LocaleAware(child: SensorsScreen()),
'/experiments': (context) =>
Expand Down
Loading
Loading