# pH Sensor

The pH sensor provides water quality measurement critical for environmental monitoring and anomaly detection.

### Hardware Specifications

<table id="bkmrk-parametervaluemodeld"><colgroup><col></col><col></col></colgroup><tbody><tr><th>Parameter

</th><th>Value

</th></tr><tr><td>Model

</td><td>DFRobot SEN0161 (Analog pH meter)

</td></tr><tr><td>Interface

</td><td>Analog ADC

</td></tr><tr><td>Reference Voltage

</td><td>3.3V or 5.0V (configurable)

</td></tr><tr><td>Output Range

</td><td>0-5V analog

</td></tr><tr><td>Measurement Range

</td><td>0-14 pH units

</td></tr><tr><td>Accuracy

</td><td>±0.1 pH @ 25°C

</td></tr><tr><td>Sample Rate

</td><td>Configurable (40 samples for averaging)

</td></tr></tbody></table>

## Calibration Model

The sensor uses linear voltage-to-pH conversion:

```
pH = (Voltage / Reference_Voltage) × Slope + Offset
```

**Default Parameters for SEN0161**<span style="white-space: pre-wrap;"> @ 25°C:</span>

- **Slope**: 3.5
- **Offset**: Variable (user calibration)

## Data Structure

```c
typedef struct {
    // Raw ADC Reading
    uint16_t raw_value;                // Raw ADC value
    
    // Calculated Values
    float voltage;                     // Converted voltage (0-5V)
    float ph_value;                    // Calculated pH (0-14)
    float reference_voltage;           // ADC reference (typically 3.3V or 5.0V)
    
    // Calibration Parameters
    ph_calibration_t calibration;      // { offset: float, slope: float }
    
    // Averaging Buffer (Noise Filtering)
    uint16_t sample_buffer[40];        // Last 40 samples
    uint8_t sample_index;              // Current position in buffer
    uint8_t samples_collected;         // Total samples collected (0-40)
} ph_sensor_t;
```

## Initialization &amp; Usage

### Initialize pH Sensor

```c
ph_sensor_t ph_sensor;
ph_sensor_init(&ph_sensor, 3.3f);  // 3.3V reference voltage
```

### Poll pH Sensor

```c
result_t ph_result = poll_ph_sensor(&ph_sensor);

if (ph_result == RESULT_OK) {
    float ph_value = ph_sensor.ph_value;
    float voltage = ph_sensor.voltage;
}
```

### Manual Sample Addition

```c
// For manual sampling at regular intervals
uint16_t adc_reading = 2048;  // Example ADC value
ph_sensor_add_sample(&ph_sensor, adc_reading);
```

## Validation

```c
result_t validate_ph_value(float ph_value);
// Returns RESULT_OK if 0 <= ph_value <= 14
// Returns RESULT_ERR_INVALID_DATA otherwise
```

## Sample Averaging Strategy

<table id="bkmrk-parametervaluesample"><colgroup><col></col><col></col></colgroup><tbody><tr><th>Parameter

</th><th>Value

</th></tr><tr><td>Sample Buffer Size

</td><td>40 samples

</td></tr><tr><td>Method

</td><td>Circular buffer moving average

</td></tr><tr><td>Purpose

</td><td>Noise filtering and stable readings

</td></tr><tr><td>Typical Update Latency

</td><td>40ms-800ms

</td></tr></tbody></table>

### Averaging Algorithm

```
1. ADC sample added to circular buffer
2. All 40 samples averaged together
3. Averaged value converted to voltage
4. Voltage converted to pH via calibration
```

## Two-Point Calibration Procedure

### Step 1: Neutral Point (pH 7.0)

```
1. Immerse electrode in pH 7.0 buffer solution
2. Wait for stable reading (~2 minutes)
3. Record voltage: V_neutral
4. Calculate offset adjustment
```

### Step 2: Slope Calibration (pH 4.0 or 10.0)

```
1. Immerse electrode in second known pH solution
2. Wait for stable reading
3. Record voltage: V_reference
4. Calculate slope from two points:
   slope = (pH_reference - 7.0) / (V_reference - V_neutral)
```

## Protobuf Message Format

```protobuf
message SensorBoardPHInfo {
    float ph_value;
    float voltage;
    SensorState state;
    PHErrorCode error_code;
}

enum PHErrorCode {
    PH_NO_ERROR = 0;
    PH_COMMUNICATION_FAILURE = 1;
    PH_INVALID_DATA = 2;
}
```

## Error Handling

```c
if (ph_result == RESULT_ERR_UNIMPLEMENTED) {
    // Hardware not connected
    diagnostics.ph_sensor.state = SensorState_SENSOR_IDLE;
    diagnostics.ph_sensor.error_code = PHErrorCode_PH_COMMUNICATION_FAILURE;
} else if (ph_result == RESULT_OK) {
    if (validate_ph_value(ph_sensor.ph_value) == RESULT_OK) {
        diagnostics.ph_sensor.state = SensorState_SENSOR_OPERATING;
        diagnostics.ph_sensor.error_code = PHErrorCode_PH_NO_ERROR;
    } else {
        // Invalid data from sensor (out of 0-14 range)
        diagnostics.ph_sensor.state = SensorState_SENSOR_ERROR;
        diagnostics.ph_sensor.error_code = PHErrorCode_PH_INVALID_DATA;
    }
}
```

## Integration Notes

- Single sensor instance in main application
- Updates transmitted to network at main loop interval (5 seconds default)
- Temperature compensation not currently implemented (assumes ~25°C)
- Sample averaging reduces noise but introduces ~40ms latency per update
- Electrode response time: ~100-300ms depending on pH change magnitude