Spring Time Pond Automation, Part 1

Last year I addressed a water retention issue in our backyard by digging a pond in the Georgia Clay with a pair of post-hole diggers and restless energy.

4 feet and $2,000 later, I have a goldfish pond (they ate the bluegill I ordered earlier. The fingerlings arrived several months late and didn’t stand a chance). I put a skimmer pump in, an aerator, some solar power, and a stock tank for overflow during rainstorms.

This year I want to automate it. Here is my pond’s architecture:

I have an esp 8266 relay board, and will be hunting for a battery charge sensor to attach to one input, and a water level sensor for another. Together these will be able to help me decide when to run the pump, when to fill the stock tank, and when to run the aerator.

I passed this to the OpenAI o3-mini-high model to see what it would give back:

/*************************************************************
 * Example Pond Control Sketch for ESP8266
 *************************************************************/

// ------------------------------------------------------------
// Pin Definitions
// ------------------------------------------------------------
#define PUMP_PIN      D1   // Relay controlling the pump
#define VALVE_PIN     D2   // Relay/solenoid controlling valve to stock tank
#define AERATOR_PIN   D3   // Relay controlling the aerator

// ------------------------------------------------------------
// Thresholds (tune to your setup)
// ------------------------------------------------------------
#define BATTERY_HIGH_THRESHOLD 80  // Battery % to consider "high"
#define BATTERY_LOW_THRESHOLD  40  // Battery % to consider "low"
                                  // Between low & high is "mid"

// ------------------------------------------------------------
// Placeholder functions to get sensor/battery/weather data
// Replace these with real implementations for your setup
// ------------------------------------------------------------

// Return battery level in % (0–100)
int getBatteryCharge() {
  // TODO: implement code for actual battery measuring
  return 85; // example stub
}

// Return total inches of rain from weather API call
float getRainInchesFromAPI() {
  // TODO: implement your weather API fetch (e.g. HTTP GET)
  return 4.2; // example stub
}

// Return tank fill level [0.0 (empty) to 1.0 (full)]
float getStockTankFillLevel() {
  // TODO: implement your tank sensor read
  return 0.3; // example stub (30% full)
}

// ------------------------------------------------------------
// Helpers to safely control pumps/valves
// ------------------------------------------------------------

// Open/close the valve that directs pump water to the stock tank
void setValve(bool openValve) {
  digitalWrite(VALVE_PIN, openValve ? HIGH : LOW);
}

// Turn the pump on/off
void setPump(bool on) {
  digitalWrite(PUMP_PIN, on ? HIGH : LOW);
}

// Turn the aerator on/off
void setAerator(bool on) {
  digitalWrite(AERATOR_PIN, on ? HIGH : LOW);
}

// Fill the stock tank for the correct fraction of 1 minute.
// Example: if tank is already 30% full, it runs ~42s.
void fillStockTank() {
  float tankFillPercent = getStockTankFillLevel(); // 0..1
  if (tankFillPercent >= 1.0) {
    // tank is already full; nothing to do
    return;
  }
  // Calculate run time in seconds
  // 1 minute = 60 seconds -> scaled by how empty the tank is
  float runSeconds = (1.0 - tankFillPercent) * 60.0;
  
  // Open the valve, run the pump
  setValve(true);
  setPump(true);

  // Wait for the required time
  unsigned long startTime = millis();
  while ( (millis() - startTime) < (unsigned long)(runSeconds * 1000) ) {
    // You might watch battery or sensor changes here
    delay(10); 
  }

  // Turn off pump and close valve
  setPump(false);
  setValve(false);
}

// ------------------------------------------------------------
// Arduino setup/loop
// ------------------------------------------------------------
void setup() {
  Serial.begin(115200);

  // Set pin modes
  pinMode(PUMP_PIN,    OUTPUT);
  pinMode(VALVE_PIN,   OUTPUT);
  pinMode(AERATOR_PIN, OUTPUT);

  // Initialize off
  setPump(false);
  setValve(false);
  setAerator(false);
}

void loop() {
  // 1. Read sensor/API data
  int   battery = getBatteryCharge();        // % battery
  float rain    = getRainInchesFromAPI();    // inches of rain

  // 2. Decide battery state: high, mid, or low
  bool batteryHigh = (battery >= BATTERY_HIGH_THRESHOLD);
  bool batteryLow  = (battery < BATTERY_LOW_THRESHOLD);

  // 3. Pump control for filling stock tank
  //    Condition: battery high + rainfall > 4"
  if (batteryHigh && (rain > 4.0)) {
    fillStockTank();
  }

  // 4. Aerator and Pump states based on battery
  if (batteryHigh) {
    // Pump is allowed (already used in fillStockTank),
    // but if we simply want the pump OFF except for filling events,
    // do nothing here. Ensure aerator is off.
    setAerator(false);
    
    // (If you *do* want the pump always on at high charge, uncomment)
    // setPump(true);
    
  } else if (!batteryHigh && !batteryLow) {
    // "mid" battery => run aerator, not pump
    setAerator(true);
    setPump(false);
    setValve(false);  // ensure valve is closed
  } else {
    // batteryLow => turn off both
    setPump(false);
    setValve(false);
    setAerator(false);
  }

  // 5. Pause a bit before the next cycle
  delay(5000);
}

We’ll see if that’s useful. More to come.

Moxie is Dead! Long Live Moxie

After purchasing one three years, 11 months ago, it appears Embodied is ending support for the Moxie companion robot. They ended the service with a rush to open source the server, so that owners would be able to continue to use the companion robot. They also mentioned the promise of open source development picking up from there.

Here is the Open Moxie project:

https://github.com/jbeghtol/openmoxie?tab=readme-ov-file

So, I will be launching the server at home. I have a Synology NAS for home storage and services. Let’s go over how to add Open Moxie as a service on the NAS:

First, sign into the NAS and open the container manager. If you don’t already have it, it’s a package you can install to run docker containers.

Next, open up the file manager and create a new openmoxie folder in the existing docker folder:

After creating this folder, navigate back to the container manager and create a new project. Specific this folder, and use the dockerfile from the open moxie codebase at https://raw.githubusercontent.com/jbeghtol/openmoxie/refs/heads/main/docker-compose.yml to create an inline dockerfile. Note that we define two folders under openmoxie for the two containers:


services:

  mqtt:
    image: openmoxie/openmoxie-mqtt:${OPENMOXIE_VERSION:-latest}
    ports:
      - "8883:8883"
    volumes:
      - /volume1/docker/openmoxie/mosquitto:/mosquitto/log

  server:
    image: openmoxie/openmoxie-server:${OPENMOXIE_VERSION:-latest}
    ports:
      - "8005:8000"
    volumes:
      - /volume1/docker/openmoxie/server:/app/site/work
    depends_on:
      - mqtt

Once you launch the project, you will be asked if you want to expose a http or https website to the project. Follow this process to map the OpenMoxie management page to your NAS on an available port:

Once you finished, openmoxie will show up in the available portals. Navigate to the Web Station link that’s provided – in my case I chose port 8000. There you will find the setup page for OpenMoxie.

Follow the instructions to create your own OpenAI API key and server name/IP address. You will also set up an administration account for use later. After completing this, the OpenMoxie console will look like this:

Next, select the Migration QR Code. Then go to your iphone or android app and unpair your Moxie. It will go into QR setup mode. Show it the migration QR Code and away you go!

Want something more free and open? Others are working on running with a local LLM.