Can’t get Keyestudio TDS sensor calibrated with 3.3v microcontroller

Just got a KS0429 keyestudio TDS Meter V1.0 that I’ve been trying to setup and calibrate for days following the sample code in the sensor’s wiki without much luck.

The problem I have is that the TDS readings are off. I am using a 1000ppm TDS calibration solution (NaCl at 25C/77F, 90ml).

I wired the black cable from the sensor to the photon’s ground pin, the red cable to the photon’s VIN pin and the yellow cable to the photon’s A0 pin.

When introducing the sensor in the calibration solution I get the following readings:
serial

raw:1287   voltage:1.04V      compensationCoefficient:1.00   compensationVoltage:1.04   TDS Value:380.65ppm
raw:1282   voltage:1.03V      compensationCoefficient:1.00   compensationVoltage:1.03   TDS Value:379.13ppm
raw:1287   voltage:1.04V      compensationCoefficient:1.00   compensationVoltage:1.04   TDS Value:380.65ppm
raw:1287   voltage:1.04V      compensationCoefficient:1.00   compensationVoltage:1.04   TDS Value:380.65ppm
raw:1281   voltage:1.03V      compensationCoefficient:1.00   compensationVoltage:1.03   TDS Value:378.82ppm
raw:1285   voltage:1.04V      compensationCoefficient:1.00   compensationVoltage:1.03   TDS Value:380.04ppm
raw:1283   voltage:1.03V      compensationCoefficient:1.00   compensationVoltage:1.03   TDS Value:379.43ppm

This is way off from 1000ppm.

I checked the output voltage from the sensor and I saw a 1.038v readings coming out from the sensor yellow cable.

I also took a reading coming out of the VIN pin just to see how much voltage was being sent to the sensor and it read 4.74v. Not sure if this would mean I should change “const float VOLTAGE_CONSTANT = 4.74/4095” although I tried that and the ppm value is still off.

firmware

#define TdsSensorPin A0
#define VREF 3.3 // analog reference voltage(Volt) of the ADC
#define SCOUNT 30 // sum of sample point
#define ADCRANGE 4096.0 // 1024.0
#define KVALUE 0.5 // 2.12

const float VOLTAGE_CONSTANT = 3.3/4095;

int analogBuffer[SCOUNT]; // store the analog value in the array, read from ADC
int analogBufferTemp[SCOUNT];
int analogBufferIndex = 0, copyIndex = 0;
float averageVoltage = 0, tdsValue = 0;

float temperature = 24.9;
float median_raw = 0;

char vitalsvalue[255];
unsigned long VITALS_INTERVAL = 60000;
unsigned long vitalsStart = 0; // the time the vitals check started
bool vitalsSuccess;

void setup() {
  Serial.begin(9600);
  Particle.variable("vitalsvalue", vitalsvalue, STRING);
}

void loop() {   
   static unsigned long printTimepoint = millis();
   if(millis()-printTimepoint > 800U)
   {
      printTimepoint = millis();
      
      int raw_reading = analogRead(TdsSensorPin);
      float measured_voltage = VOLTAGE_CONSTANT * raw_reading;
      float compensationCoefficient = (1.0 - (0.02*(temperature-25.0)));    //temperature compensation formula: fFinalResult(25^C) = fFinalResult(current)/(1.0+0.02*(fTP-25.0));
      float compensationVoltage=measured_voltage/compensationCoefficient;  //temperature compensation
      tdsValue=(133.42*compensationVoltage*compensationVoltage*compensationVoltage - 255.86*compensationVoltage*compensationVoltage + 857.39*compensationVoltage)*KVALUE; //convert voltage value to tds value
      
      Serial.print("raw:");
      Serial.print(raw_reading);
      Serial.print("   voltage:");
      Serial.print(measured_voltage);
      Serial.print("V   ");
      Serial.print("   compensationCoefficient:");
      Serial.print(compensationCoefficient);
      Serial.print("   compensationVoltage:");
      Serial.print(compensationVoltage);
      Serial.print("   TDS Value:");
      Serial.print(tdsValue);
      Serial.println("ppm");
      
      publishVitals();
   }
}

int publishVitals() {
  char jsonData[255];
  // assuming lightvalue to be char* or similar
  snprintf(jsonData, sizeof(jsonData), "{ \"status\": \"active\", \"raw\": \"%f\", \"voltage\": \"%f\", \"tds\": \"%f\", \"temperature\": \"%f\" }", median_raw, averageVoltage, tdsValue, temperature);
  strcpy(vitalsvalue, jsonData);
  
  if ( (millis() - vitalsStart) >= VITALS_INTERVAL ) {      // Completed
    
    // This prints the value to the USB debugging serial port (for optional debugging purposes)
    Serial.printlnf("data: %s", vitalsvalue);
      
    vitalsStart += VITALS_INTERVAL; // this prevents drift in the intervals
    
    vitalsSuccess = Particle.publish("vitals", vitalsvalue, PRIVATE);
      
    Serial.printlnf("vitals published to the cloud: %s", (vitalsSuccess ? "success" : "failed"));
    
    return 1;
  } else {
    return 0;   
  }
}


int getMedianNum(int bArray[], int iFilterLen) {
  int bTab[iFilterLen];
  for (byte i = 0; i<iFilterLen; i++)
    bTab[i] = bArray[i];
  int i, j, bTemp;
  for (j = 0; j < iFilterLen - 1; j++) 
  {
    for (i = 0; i < iFilterLen - j - 1; i++) 
    {
      if (bTab[i] > bTab[i + 1]) 
      {
        bTemp = bTab[i];
        bTab[i] = bTab[i + 1];
        bTab[i + 1] = bTemp;
      }
    }
  }
  if ((iFilterLen & 1) > 0)
    bTemp = bTab[(iFilterLen - 1) / 2];
  else
    bTemp = (bTab[iFilterLen / 2] + bTab[iFilterLen / 2 - 1]) / 2;
  return bTemp;
}