ULPSM SO2 968-006 reading way above possible values

Hi everyone,

I’ve been trying to get the 968-006 up and running but seem to have run into a small problem, the readings it give off are way above the possible values. After more than 1 hour of ‘Power-On Stabilization Time’ the sensor returns a measured ppm of around 5. For the record 0.5 ppm SO2 will kill you within 3 hours.

I used the library created by the developers: GitHub - SPEC-Sensors/ULPSM: Public Arduino Library for ULPSM (ultra-low power sensor module) or SDK (sensor development kit)
And used the recommended 2x 10k ohm voltage ladders, as can be seen in my setup in the image blow.

I modified the code slightly to work with the voltage ladder, this is the code I use:

  ULP.cpp - Library for reading SPEC Sensors ULP.
  Revised by David E. Peaslee, May 22, 2020.
  Created by David E. Peaslee, OCT 27, 2016.
  Released into the public domain.

#include "Arduino.h"
#include "ULP.h"

// These constants won't change.  They're used to give names to the pins used and to the sensitivity factors of the sensors:

const int C1 = A0;
const int T1 = A3;
//averaging times, keep these low, so that the ADC read does not overflow 32 bits. For example n = 5 reads ADC 4465 times which could add to 22bit number.
const int n = 5; //number of seconds to read gas sensor
const int m = 1; //number of seconds to read temperature sensor
const int s = 10; //number of seconds to read all sensors should be greater than n+m+1

const float Sf1 = 11.61; //nA/ppm change this to match your barcode!!!!

unsigned long etime;

SO2 sensor1(C1, T1, Sf1); 

//  Include these if using different boards with different voltages

void setup() {
  //uncomment only after you have connected 3.3V to Aref on the arduino
  sensor1.pVcc = 3.31;  //analogRead Reference Voltage, equal to pVsup when analogReference uses same voltage as sensor power
  //sensor1.pVcc = 5.04;  //analogRead Reference Voltage, maybe measure Aref??
  sensor1.pVsup = 3.31;  //voltage supplied to V+ of ULP, default is 3.3 Volts, probably should measure this as well.
  sensor1.pVref = 1650;

  Serial.begin(9600);    // initialize serial communications at 9600 bps:

  Serial.println("Setting Up.");

  Serial.print("Vsup for all sensors = ");
  Serial.print("Vcc for all sensors = ");
  Serial.print("Vref for sensor 1 = ");

  //  Using resistor values from board R1, R2, R3 are for setting pVref and Bias, while R6 sets the gain
  //  If using modified or custom boards set Vref and Gain like this
  //long int R2 = 1000;  //Assumes R1 and R3 are 1 MOhms in resistor ladder
  //float bias = -25.0
  //sensor2.setVref(bias, R2);
  //sensor2.pGain = 100000; //resistor R6

  //if you know the V_ref replace the following code...
  Serial.println("Remove Sensor.");
  if (sensor1.OCzero(n)) {
    Serial.print("Vref new = ");
  } else {
    Serial.println("Recheck Settings, Zero out of range");
    while (1) {
  //...with this code and your measured value of new Vref
  //sensor1.pVref_set = ????

  //sensor1.setXSpan();                                //Must have previously zeroed in clean air, returns new span factor.

  //When calibrating the temperature use "LOW"/"HIGH" for the temperature range ie .setTSpan(40.2, "HIGH") where T is the current high temperature
  //sensor1.setTSpan((71 - 32.0) * 5.0 / 9.0, "LOW");

  Serial.println("Finished Setting Up, Replace Sensor Now.");
  Serial.println("T1, mV, nA, C1");
  etime = millis();

void loop() {
  while (Serial.available()) {
    if (Serial.read() == 'Z') {
      sensor1.zero(); //Uses last values read of Izero and Tzero
      Serial.print("Izero, Tzero: ");
      Serial.print(", ");

  if (millis() - etime > (s * 1000)) {//also handles case where millis rolls over, but may cause timing issue.
    etime = millis();


    Serial.print(sensor1.convertT('C')); //use 'C' or 'F' for units
    Serial.print(", ");
    Serial.print(", ");
    Serial.print(", ");
    Serial.println(sensor1.convertX('M')); //use 'M' or 'B' for units or Serial.println(sensor1.pX); to just print ppb



Could anybody help me figure out why the ppm readings are so high? Thanks in advance!

Hello @AlexDG,

Maybe a silly question, but have you tried sensor1.pVref = 1.650;?

The SO2 sensor is indicated as being delivered with a 100KV/A gain factor and sensitivity of 30nA/PPM, resulting in something like 3mV of signal per PPM of analyte concentration. 3mV is basically 1 ADC code on a 10-bit arduino with 3.3V reference, so part of the problem at least would appear to be insufficient gain. Why the NO2 variant with what appears to be identical sensitivity and range specs would ship with 5x the gain isn’t obvious, but swapping out R6 for a higher value might be useful if one’s going to stick with the same hardware otherwise.

Thanks for the observation! I assume the R6 here refers to the same R6 that is noted in the schematic listed on the Github (ULPSM/ULPSM_R11_SCH_RELEASE.pdf at master · SPEC-Sensors/ULPSM · GitHub). I have no experience in modifying pcbs, so I suspect swapping out the resistor can be quite a challenge. Do you have a suggestion on how I can achieve the same result without modifying the pcb?

The library dictates that Vref is set in mV, setting it to 1.650 results in the zeroing failing.

An instrumentation amplifier could be used to buffer and amplify the differential output signal. The AD623BNZ is an example of such a device.

This would also allow getting rid of the 10K reference divider, allowing the on-board sensor biasing to function as intended. And of course, the software would need to be adjusted accordingly.