Skip to content

Instantly share code, notes, and snippets.

@lptr
Created September 26, 2025 06:54
Show Gist options
  • Select an option

  • Save lptr/ccdc23f15069fada8be5228f144823e7 to your computer and use it in GitHub Desktop.

Select an option

Save lptr/ccdc23f15069fada8be5228f144823e7 to your computer and use it in GitHub Desktop.
FDC1004 for soil moisture IDC sensor
//////////////////////////////////////////////////////////////////////////////////////////
//
// Basic Single channel demo for the FDC1004 capacitance sensor breakout board
//
// Author: Ashwin Whitchurch
// Copyright (c) 2018-2025 Protocentral Electronics
//
// This example measures capacitance on CHANNEL0 with
// automatic CAPDAC adjustment.
//
// Arduino connections:
//
// Arduino FDC1004 board
// ------- -------------
// 5V -> Vin
// GND -> GND
// A4 -> SDA
// A5 -> SCL
//
// For boards with multiple I2C interfaces (ESP32, Arduino Mega, etc.):
// You can use Wire1 or other I2C interfaces by modifying the constructor.
// See the constructor examples below.
//
// This software is licensed under the MIT License(http://opensource.org/licenses/MIT).
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// For information on how to use, visit https://github.com/protocentral/ProtoCentral_fdc1004_breakout
//
/////////////////////////////////////////////////////////////////////////////////////////
#include <Wire.h>
#include <Protocentral_FDC1004.h>
// Constructor Options - Choose one of the following:
// Option 1: Default Wire interface (backwards compatible)
FDC1004 capacitanceSensor(FDC1004_RATE_100HZ);
// Option 2: Specify custom I2C interface (for boards with multiple I2C ports)
// FDC1004 capacitanceSensor(&Wire1, FDC1004_RATE_100HZ);
// Option 3: Alternative syntax with TwoWire as third parameter
// FDC1004 capacitanceSensor(FDC1004_RATE_100HZ, FDC1004_I2C_ADDRESS, &Wire1);
// Option 4: Custom I2C address with default Wire
// FDC1004 capacitanceSensor(FDC1004_RATE_100HZ, 0x51); // Different address
void setup() {
Serial.begin(115200);
// Initialize I2C interface
// Note: If using Wire1 or custom I2C interface, initialize that instead
// Example for ESP32 with custom pins: Wire1.begin(21, 22);
Wire.begin(4, 5);
Serial.println("FDC1004 Capacitance Sensor");
Serial.println("==========================");
Serial.println("Using default Wire interface");
Serial.println();
// Initialize the sensor with error checking
if (capacitanceSensor.begin()) {
Serial.println("✓ FDC1004 sensor initialized successfully");
} else {
Serial.println("✗ Failed to initialize FDC1004 sensor");
Serial.println("Check wiring and restart Arduino");
while (1) {
delay(1000);
}
}
// Verify device connection
if (capacitanceSensor.isConnected()) {
Serial.println("✓ FDC1004 device is responding");
} else {
Serial.println("✗ FDC1004 device not responding");
Serial.println("Check I2C connections");
}
Serial.println();
Serial.println("Starting continuous measurements on Channel 0...");
Serial.println("Capacitance(pF)\tCAPDAC\tStatus");
Serial.println("----------------------------------------------");
}
void loop() {
static unsigned long lastMeasurement = 0;
// Hysteresis & timing params
const float CAPDAC_STEP_PF = 3.125f; // 1 LSB of CAPDAC
const float LOWER_HYST_PF = 6.0f; // go down if residual < this
const uint8_t DOWN_STICKY_SAMPLES = 3; // require N consecutive low-residual samples
static uint8_t lowResidualCount = 0;
// Track CAPDAC; sync with what the driver actually used on the last read
static int capdacSetting = 0;
static bool capdacInit = false;
// Take measurement every 250ms
if (millis() - lastMeasurement >= 250) {
fdc1004_capacitance_t m = capacitanceSensor.getCapacitanceMeasurement(FDC1004_CHANNEL_0);
// One-time sync (first sample) or re-sync every time to follow library auto-increase
if (!capdacInit) {
capdacSetting = m.capdac_used;
capdacInit = true;
} else {
// If the library auto-increased CAPDAC, follow it
if (m.capdac_used != capdacSetting) {
capdacSetting = m.capdac_used;
lowResidualCount = 0; // reset the down hysteresis
}
}
// Print timestamp
// Serial.print(millis());
// Serial.print("\t\t");
if (!isnan(m.capacitance_pf)) {
// Compute residual inside the ADC window
float residual = m.capacitance_pf - capdacSetting * CAPDAC_STEP_PF;
// Pretty print values
Serial.print(">");
Serial.print("capacitance:");
Serial.print(m.capacitance_pf, 4);
Serial.print(",capdac:");
Serial.print(capdacSetting);
Serial.print("\n");
// --- Auto-decrease logic with hysteresis ---
// Count consecutive "comfortably low" residual samples
if (residual < LOWER_HYST_PF) {
if (lowResidualCount < 255) lowResidualCount++;
} else {
lowResidualCount = 0;
}
bool steppedDown = false;
if (lowResidualCount >= DOWN_STICKY_SAMPLES && capdacSetting > 0) {
// try one step down
uint8_t nextCapdac = static_cast<uint8_t>(capdacSetting - 1);
if (capacitanceSensor.setCapdac(FDC1004_CHANNEL_0, nextCapdac) == FDC1004_SUCCESS) {
capdacSetting = nextCapdac;
steppedDown = true;
lowResidualCount = 0; // re-arm hysteresis
}
}
// Status text
if (m.capdac_out_of_range) {
Serial.println("CAPDAC ADJUST (up by lib)");
} else if (steppedDown) {
Serial.println("CAPDAC STEP DOWN");
} else {
Serial.println("OK");
}
} else {
Serial.println("ERROR\t\t-\tMEASUREMENT FAILED");
lowResidualCount = 0; // be conservative
}
lastMeasurement = millis();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment