commit 1a208fef0ad0209f70e09ff803bd8c891e81a047 Author: philippd Date: Tue May 14 22:03:17 2024 +0200 Dateien nach "/" hochladen diff --git a/README.md b/README.md new file mode 100644 index 0000000..05239dc --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# RIU (Remote Interface Unit) +## Setup +### Hardware +- Arduino Uno +- BMP280 breakout board (Pressure and Temperature sensor) +#### Wiring +- BMP280 VCC -> Arduino 3.3V +- BMP280 GND -> Arduino GND +- BMP280 SDA -> Arduino SDA +- BMP280 SCL -> Arduino SCL +### Software +- Arduino IDE +- Adafruit BMP280 library +## Usage +- Upload the code to the Arduino +- Open the serial monitor and type commands +- Alternatively, directly send commands to the Arduino via serial communication \ No newline at end of file diff --git a/RIU-datasheet.md b/RIU-datasheet.md new file mode 100644 index 0000000..0493882 --- /dev/null +++ b/RIU-datasheet.md @@ -0,0 +1,135 @@ +# Tübingen Instruments RIU-9000 Datasheet + +--- + +## Manufacturer: TI (Tübingen Instruments) + +### Overview +The RIU-9000 is a versatile Remote Interface Unit (RIU) designed for spacecraft sensor management. This device interfaces with an Adafruit BMP280 sensor to provide accurate pressure and temperature readings. The RIU-9000 allows users to configure sensor parameters and retrieve sensor data through a structured 4-step communication protocol. + +### Features +- **Sensor Support**: Integrated support for BMP280 pressure and temperature sensor. +- **Configurable Parameters**: Offset and sampling rate adjustments for both pressure and temperature readings. +- **Reliable Communication**: 4-step communication protocol to ensure data integrity. +- **Error Handling**: Error messages for invalid commands or parameters. +- **Industry Standard Data Format**: Sensor data is sent in binary format representing 32 bit IEEE-754 Floating Point values. + +--- + +### Commands and Responses + +#### General Format +Commands should be sent as strings over the serial interface at 115200 baud rate. Commands are case-sensitive and must be followed by a newline character (`\n`). Parameters and values should be separated by a space. Responses from the RIU-9000 will also be in string format. + +#### STARTUP +- **Command**: `STARTUP` +- **Response**: `READY - Tübingen Instruments RIU-9000` +- **Description**: Initializes the device. This command must be sent before any other commands are accepted. + +#### GET_SENSOR +- **Command**: `GET_SENSOR [COMMAND] [SENSOR]` +- **Description**: Initiates a 4-step communication process to read sensor data. +- **Steps**: + 1. **REQUEST**: Command the device to read sensor value. + - **Command**: `GET_SENSOR REQUEST [SENSOR]` + - **Response**: `OK` or `ERROR` + 2. **CONFIRM**: Ask if the device requested the data from the sensor. + - **Command**: `GET_SENSOR CONFIRM [SENSOR]` + - **Response**: `TRUE` or `FALSE` + 3. **CHECK**: Ask if the data is present now. + - **Command**: `GET_SENSOR CHECK [SENSOR]` + - **Response**: `TRUE` or `FALSE` + 4. **SEND**: Command the device to send the data to the user. + - **Command**: `GET_SENSOR SEND [SENSOR]` + - **Response**: `[DATA]` or `FALSE` + 5. **CANCEL**: Cancel the sensor reading request. + - **Command**: `GET_SENSOR CANCEL [SENSOR]` + - **Response**: `OK` + +#### SET_PARAMETER +- **Command**: `SET_PARAMETER [PARAM] [VALUE]` +- **Response**: `OK` or `ERROR: UNKNOWN PARAMETER` +- **Parameters**: + - `PRESSURE_OFFSET`: Sets the offset for pressure readings. (In hPa) + - `TEMPERATURE_OFFSET`: Sets the offset for temperature readings. (In °C) + - `PRESSURE_SAMPLING`: Sets the oversampling rate for the pressure sensor (`SAMPLING_NONE`, `SAMPLING_X1`, `SAMPLING_X2`, `SAMPLING_X4`, `SAMPLING_X8`, `SAMPLING_X16`). + - `TEMPERATURE_SAMPLING`: Sets the oversampling rate for the temperature sensor (`SAMPLING_NONE`, `SAMPLING_X1`, `SAMPLING_X2`, `SAMPLING_X4`, `SAMPLING_X8`, `SAMPLING_X16`). + +#### GET_PARAMETER +- **Command**: `GET_PARAMETER [PARAM]` +- **Response**: `[VALUE]` or `ERROR: UNKNOWN PARAMETER` +- **Parameters**: + - `PRESSURE_OFFSET`: Retrieves the current pressure offset. + - `TEMPERATURE_OFFSET`: Retrieves the current temperature offset. + - `PRESSURE_SAMPLING`: Retrieves the current pressure oversampling rate. + - `TEMPERATURE_SAMPLING`: Retrieves the current temperature oversampling rate. + +#### RESET_SENSORS +- **Command**: `RESET_SENSORS` +- **Response**: `OK` +- **Description**: Resets the request count and communication state. + +#### Communication Errors +- **BUSY**: Device is busy. Every 20th request, the device will respond with `BUSY` and not process the command. +- **ERROR**: Generic error message for invalid commands or failed operations. +- **FALSE**: Response for confirmation checks that fail. +- **[NO RESPONSE]**: After 100 requests, the device will stop responding to commands until a `RESET_SENSORS` command is issued. + +--- + + +### Example Usage + +#### Initialization +``` +User: STARTUP +Device: READY +``` + +#### Setting Parameters +``` +User: SET_PARAMETER PRESSURE_OFFSET 10 +Device: OK + +User: SET_PARAMETER PRESSURE_SAMPLING SAMPLING_X4 +Device: OK +``` + +#### Getting Parameters +``` +User: GET_PARAMETER PRESSURE_OFFSET +Device: 10 + +User: GET_PARAMETER PRESSURE_SAMPLING +Device: SAMPLING_X4 +``` + +#### Reading Sensor Data +``` +User: GET_SENSOR REQUEST PRESSURE +Device: OK + +User: GET_SENSOR CONFIRM PRESSURE +Device: OK + +User: GET_SENSOR CHECK PRESSURE +Device: FALSE + +User: GET_SENSOR CHECK PRESSURE +Device: TRUE + +User: GET_SENSOR SEND PRESSURE +Device: [Binary Data] +``` + +#### Resetting Sensors +``` +User: RESET_SENSORS +Device: OK +``` + +--- + +For more detailed information and troubleshooting, please refer to the official TI (Tübingen Instruments) RIU-9000 documentation. + +--- \ No newline at end of file diff --git a/RIU.ino b/RIU.ino new file mode 100644 index 0000000..57e6c35 --- /dev/null +++ b/RIU.ino @@ -0,0 +1,305 @@ +#include +#include +#include + +// BMP280 Sensor +Adafruit_BMP280 bmp; // use I2C interface +Adafruit_Sensor *bmp_temp = bmp.getTemperatureSensor(); +Adafruit_Sensor *bmp_pressure = bmp.getPressureSensor(); + +// Constants +const int MAX_REQUESTS = 100; +const int BUSY_THRESHOLD = 20; + +// Parameters +int pressureOffset = 0; +Adafruit_BMP280::sensor_sampling pressureSampling = Adafruit_BMP280::SAMPLING_X1; +int temperatureOffset = 0; +Adafruit_BMP280::sensor_sampling temperatureSampling = Adafruit_BMP280::SAMPLING_X1; + +// State Variables +int requestCount = 0; +bool startupReceived = false; +String lastSensorValue = ""; + +// Communication state for 4-step process +struct CommunicationState { + bool requestConfirmed = false; + bool confirmConfirmed = false; + bool checkConfirmed = false; +}; + +CommunicationState commState; + +// Function Prototypes +void handleCommand(String command); +void handleGetSensor(String command, String sensor); +void handleSetParameter(String param, String value); +void handleGetParameter(String param); +void resetSensors(); +String getRawSensorData(String sensor); +bool randomFailure(float chance); +String* splitString(String data, char separator, int* size); +Adafruit_BMP280::sensor_sampling getSamplingFromUserInput(String value); +String samplingToString(Adafruit_BMP280::sensor_sampling sampling); +String floatToBinary(float value); + +void setup() { + Serial.begin(115200); + while (!Serial) { + ; // Wait for serial port to connect. + } + + while (!startupReceived) { + if (Serial.available()) { + String command = Serial.readStringUntil('\n'); + if (command.startsWith("STARTUP")) { + startupReceived = true; + } + } + } + + unsigned status = bmp.begin(0x76); + if (!status) { + Serial.println(F("Could not find a valid BMP280 sensor, check wiring or try a different address!")); + while (1) delay(10); + } + + bmp.setSampling(Adafruit_BMP280::MODE_NORMAL, /* Operating Mode. */ + temperatureSampling, /* Temp. oversampling */ + pressureSampling, /* Pressure oversampling */ + Adafruit_BMP280::FILTER_X16, /* Filtering. */ + Adafruit_BMP280::STANDBY_MS_500); /* Standby time. */ + + Serial.println("READY - Tübingen Instruments RIU-9000"); +} + +void loop() { + if (Serial.available()) { + String command = Serial.readStringUntil('\n'); + delay(50); // Small delay to make communication more interesting + handleCommand(command); + } +} + +void handleCommand(String command) { + if (requestCount >= MAX_REQUESTS && !command.startsWith("RESET_SENSORS")) { + return; + } + + if (requestCount > 0 && requestCount % BUSY_THRESHOLD == 0) { + Serial.println("BUSY"); + requestCount++; + return; + } + + int size; + String* parts = splitString(command, ' ', &size); + + if (parts[0] == "GET_SENSOR" && size == 3) { + handleGetSensor(parts[1], parts[2]); + } else if (parts[0] == "SET_PARAMETER" && size == 3) { + handleSetParameter(parts[1], parts[2]); + } else if (parts[0] == "GET_PARAMETER" && size == 2) { + handleGetParameter(parts[1]); + } else if (parts[0] == "RESET_SENSORS" && size == 1) { + resetSensors(); + } else { + Serial.println("ERROR: UNKNOWN COMMAND"); + } + + delete[] parts; + requestCount++; +} + +void handleGetSensor(String command, String sensor) { + + if (command == "REQUEST") { // request data from the RUI (which will request it from the sensor) + if (!commState.requestConfirmed && randomFailure(0.1)) { + Serial.println("ERROR"); + return; + } + lastSensorValue = getRawSensorData(sensor); + Serial.println("OK"); + commState.requestConfirmed = true; + + } else if (command == "CONFIRM") { // did you request the data from the sensor? + if (!commState.confirmConfirmed && randomFailure(0.1)) { + Serial.println("FALSE"); + return; + } + if (commState.requestConfirmed) { + Serial.println("TRUE"); + commState.confirmConfirmed = true; + } else { + Serial.println("FALSE"); + } + } else if (command == "CHECK") { // did you get the data from the sensor? + if (!commState.checkConfirmed && randomFailure(0.5)) { + Serial.println("FALSE"); + } else { + if (commState.requestConfirmed) { + Serial.println("TRUE"); + commState.checkConfirmed = true; + } else { + Serial.println("FALSE"); + } + } + } else if (command == "SEND") { // send the data to the user + if (commState.requestConfirmed) { + Serial.println(lastSensorValue); + commState = CommunicationState(); // Reset communication state + } else { + Serial.println("FALSE"); + } + } else if (command == "CANCEL") { + commState = CommunicationState(); // Reset communication state + Serial.println("OK"); + } else { + Serial.println("ERROR: UNKNOWN COMMAND"); + } +} + +void handleSetParameter(String param, String value) { + if (param == "PRESSURE_OFFSET") { + pressureOffset = value.toInt(); + Serial.println("OK"); + } else if (param == "PRESSURE_SAMPLING") { + pressureSampling = getSamplingFromUserInput(value); + bmp.setSampling(Adafruit_BMP280::MODE_NORMAL, /* Operating Mode. */ + temperatureSampling, /* Temp. oversampling */ + pressureSampling, /* Pressure oversampling */ + Adafruit_BMP280::FILTER_X16, /* Filtering. */ + Adafruit_BMP280::STANDBY_MS_500); /* Standby time. */ + Serial.println("OK"); + } else if (param == "TEMPERATURE_OFFSET") { + temperatureOffset = value.toInt(); + Serial.println("OK"); + } else if (param == "TEMPERATURE_SAMPLING") { + temperatureSampling = getSamplingFromUserInput(value); + bmp.setSampling(Adafruit_BMP280::MODE_NORMAL, /* Operating Mode. */ + temperatureSampling, /* Temp. oversampling */ + pressureSampling, /* Pressure oversampling */ + Adafruit_BMP280::FILTER_X16, /* Filtering. */ + Adafruit_BMP280::STANDBY_MS_500); /* Standby time. */ + Serial.println("OK"); + } else { + Serial.println("ERROR: UNKNOWN PARAMETER"); + } +} + +void handleGetParameter(String param) { + if (param == "PRESSURE_OFFSET") { + Serial.println(pressureOffset); + } else if (param == "PRESSURE_SAMPLING") { + Serial.println(samplingToString(pressureSampling)); + } else if (param == "TEMPERATURE_OFFSET") { + Serial.println(temperatureOffset); + } else if (param == "TEMPERATURE_SAMPLING") { + Serial.println(samplingToString(temperatureSampling)); + } else { + Serial.println("ERROR: UNKNOWN PARAMETER"); + } +} + +void resetSensors() { + requestCount = 0; + commState = CommunicationState(); // Reset communication state + Serial.println("OK"); +} + +String getRawSensorData(String sensor) { + sensors_event_t temp_event, pressure_event; + + if (sensor == "PRESSURE") { + bmp_pressure->getEvent(&pressure_event); + float pressure = pressure_event.pressure + pressureOffset; + return floatToBinary(pressure); // in hPa + } else if (sensor == "TEMPERATURE") { + bmp_temp->getEvent(&temp_event); + float temperature = temp_event.temperature + temperatureOffset; + return floatToBinary(temperature); // in °C + } + + return ""; +} + +bool randomFailure(float chance) { + return random(0, 100) < (chance * 100); +} + +String* splitString(String data, char separator, int* size) { + int found = 0; + int strIndex[] = {0, -1}; + int maxIndex = data.length() - 1; + int count = 1; + + for (int i = 0; i <= maxIndex; i++) { + if (data.charAt(i) == separator) { + count++; + } + } + + String* result = new String[count]; + *size = count; + + found = 0; + for (int i = 0; i <= maxIndex && found < count; i++) { + if (data.charAt(i) == separator || i == maxIndex) { + strIndex[0] = strIndex[1] + 1; + strIndex[1] = (i == maxIndex) ? i + 1 : i; + result[found] = data.substring(strIndex[0], strIndex[1]); + found++; + } + } + + return result; +} + +Adafruit_BMP280::sensor_sampling getSamplingFromUserInput(String value) { + if (value == "SAMPLING_NONE") return Adafruit_BMP280::SAMPLING_NONE; + if (value == "SAMPLING_X1") return Adafruit_BMP280::SAMPLING_X1; + if (value == "SAMPLING_X2") return Adafruit_BMP280::SAMPLING_X2; + if (value == "SAMPLING_X4") return Adafruit_BMP280::SAMPLING_X4; + if (value == "SAMPLING_X8") return Adafruit_BMP280::SAMPLING_X8; + if (value == "SAMPLING_X16") return Adafruit_BMP280::SAMPLING_X16; + return Adafruit_BMP280::SAMPLING_NONE; +} + +String samplingToString(Adafruit_BMP280::sensor_sampling sampling) { + switch (sampling) { + case Adafruit_BMP280::SAMPLING_NONE: + return "SAMPLING_NONE"; + case Adafruit_BMP280::SAMPLING_X1: + return "SAMPLING_X1"; + case Adafruit_BMP280::SAMPLING_X2: + return "SAMPLING_X2"; + case Adafruit_BMP280::SAMPLING_X4: + return "SAMPLING_X4"; + case Adafruit_BMP280::SAMPLING_X8: + return "SAMPLING_X8"; + case Adafruit_BMP280::SAMPLING_X16: + return "SAMPLING_X16"; + default: + return "UNKNOWN_SAMPLING"; + } +} + +String floatToBinary(float value) { + union FloatUnion { + float value; + byte bytes[4]; + }; + + FloatUnion floatUnion; + floatUnion.value = value; + + String binaryString = ""; + for (int i = 3; i >= 0; i--) { + for (int j = 7; j >= 0; j--) { + binaryString += String((floatUnion.bytes[i] >> j) & 1); + } + } + + return binaryString; +} diff --git a/launch.json b/launch.json new file mode 100644 index 0000000..7e4253b --- /dev/null +++ b/launch.json @@ -0,0 +1,8 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + "version": "0.2.0", + "configurations": [ + + ] +}