> ## Content Index
> Fetch the complete content index at: https://undiluted.org/llms.txt
> Use this file to discover other available public pages before exploring further.

# PHreader - Storing PH Data into PostgreSQL
- URL: https://undiluted.org/c-software-to-store-ph-data/
- Published: 2026-09-04T12:49:23.000Z
- Updated: 2026-09-09T12:54:37.000Z
- Author: nic
- Tags: #Import 2026-09-02 19:24, C, linux, Raspberry Pi, PostgreSQL

As mentioned in section [1](https://undiluted.org/raspberry-pi-ph-circuit/), the Raspberry Pi PH Circuit (<https://undiluted.org/raspberry-pi-ph-circuit/>) was selected, incorporating a PH sensor from AtlasScientific ([https://www.atlas-scientific.com/product\_pages/kits/ph-kit.html](https://www.atlas-scientific.com/product%5Fpages/kits/ph-kit.html?ref=undiluted.org)) and a Raspberry Pi Zero.

**Summary - What PHreader is**

In short, PHreader is a piece of software written in \[C\]([https://en.wikipedia.org/wiki/C\_(programming\_language)](https://en.wikipedia.org/wiki/C%5F%28programming%5Flanguage%29?ref=undiluted.org) to interface to the PH sensor.

PHreader communicates with the sensor to pull PH data over the I²C protocol via UART.

PHreader utilizes, the [Wiring Pi Library](https://https//web.archive.org/web/20220405225008/http://wiringpi.com/wiringpi-deprecated/?ref=undiluted.org) version 2.50 (which was subsequently deprecated, but was taken over by [GC2](https://github.com/GrazerComputerClub?ref=undiluted.org) in 2024) to communicate with the sensor/circuit.

The C program communicates with the sensor, continuously in a loop as a background daemon on the raspberry pi, retrieving data and storing the [Ph](https://en.wikipedia.org/wiki/PH?ref=undiluted.org) value in a [Postgresql](https://www.postgresql.org/?ref=undiluted.org) database.

To store the sensor data in the PostgreSQL database, the software utilizes the [libpq](https://www.postgresql.org/docs/current/libpq.html?ref=undiluted.org) library.

**Configuration**

Since the software was written in C, using a Declarative, Block-Structured Configuration Format, made the mode sense.

This gave us the ability to have a configuration such as:

```
phreader = {
  debug = true;
  logfile = "/tmp/phreader.log";

  loop_interval = 3600;

  serial = {
    device = "/dev/ttyAMA0";
    baudrate = 9600;
  };

  database = {
    hostname = "";
    port = 5432;
    username = "";
    password = "";
    database = "";
    ssl_mode = false;
  };
};

```

Using [libconfig](https://hyperrealm.github.io/libconfig/?ref=undiluted.org) we can easily parse our configuration into appropriate C structures, such as:

```
typedef struct pg_config {
  const char *username;
  const char *password;
  const char *hostname;
  int port;
  const char *database;
  int ssl_mode;
} pg_config;

typedef struct serial_config {
  const char *device;
  int baudrate;
} serial_config;

typedef struct phreader_config {
  int debug;
  int loop_interval;
  const char *logfile;
  const char *pidfile;
  int valid;
  pg_config pg_config;
  serial_config serial_config;
} phreader_config;

```

We can then utilize the configuration structures, in the main daemon loop to communicate with the sensor over UART (serial) to fetch our data, and to store the PH data in the Postgresql database.

**Main Daemon Loop**

The main daemon loop is simple it does a few things, in a continuous loop:

- Open the serial port for reading

This is easily done thanks to the WiringPi library. To open the serial port, we simply do:

```
fd = serialOpen(cfg.serial_config.device, cfg.serial_config.baudrate))

```

- Initialize the WiringPi Library

This is simply a call to make the GPIO pins to have sequential numbering that the WiringPi library uses vs the physical/Broadcom pin numbers.

```
wiringPiSetup()

```

- Wake up Ph sensor circuit

For this piece, we wrote our own functions which are basically a wrapper around the Ph Circuits serial commands.

To wake up the Circuit, we simply send a 'Wake' command over serial.

```
serialPrintf(fd, "Wake\r");

```

- Switch to continuous mode

Similar to the Wake command, the circuit allows for a continuous mode. The continous mode can be used when receiving data from the circuit.

```
serialPrintf(fd, "C,%d\r", seconds);

```

- Fetch the Ph data

To fetch data, while the circuit is in continous mode, we simply send a read command and then listen for the data on the serial port.

```
serialPrintf(fd, "R\r"); // This starts our fetch
data = uartRecv(fd);

```

```
uartData * uartRecv(int fd)
{
  char *buf;
  char c;
  int count = 0;
  int iteration = 0;
  int index = 0;
  int line = 0;
  uartData *data = NULL;

  while((count = serialDataAvail(fd)) != 0)
  {
    c = serialGetchar(fd);

    if (!iteration) {
      buf = (char *)malloc((size_t)count + 1);
    }

    if (c == '\r') {
      buf[index++] = '\0';

      if (buf[0] == '*') {
        // cmd code response
        data = addUartData(data, line, "CMD_CODE", buf);
      }
      else
      {
        // actual data response
        data = addUartData(data, line, "DATA", buf);
      }

      line++;

      // realloc if we have more to read.
      if (count > 0) {
        // if realloc fails, break.
        if (realloc(buf, count) == NULL) {
          break;
        }

        index = 0;
      }
    }
    else
    {
      buf[index++] = c;
    }

    //fflush(stdout);
    iteration++;
  }

  free(buf);
  return data;
}

```

- Set back to sleep mode

This is simply a call over the Serial port in order to put the circuit back into sleep mode to save power.

```
serialPrintf(fd, "sleep\r");

```

- Close the serial port

Using the WiringPi library, we can close the serial port.

```
serialClose(fd);

```

- Connect to the Postgresql DB

Using the libpgq library, we connect to the Postgresql DB.

```
db_uri = create_pg_db_uri(cfg.pg_config);
PGconn *conn = pg_connect(db_uri);
free(db_uri);   

```

- Store the Ph data in the Postgresql DB

Using the libpgq library, we call our insert query to store our current Ph value and timestamp.

```
insert_ph(conn, ph);

```

- Close the connection to the Postgesql DB

Assuming we are connected, we can then close our DB connection, and go to sleep until the next iteration of the loop.

```
pg_close(conn);
...

sleep(cfg.loop_interval);

```

The [next step](https://undiluted.org/node-js-rest-ph-data-api/) was to expose the data using a [REST](https://en.wikipedia.org/wiki/REST?ref=undiluted.org) API.