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.

First Aid Kits Suck, Part 1

I have decided for my next reference implementation demonstrating a Rust web service API catalog that I should make a first aid kit refill service.

These things are awful. They are homogonous and full of stuff the average person will never use. The items are often expired. They are cheap. The bandages give people rashes.

So to begin with I’ll make a landing page in WordPress, with the intent of wrapping it in a business if customers agree.

Landing Page

Since I don’t have a product yet, the first landing page is more about making sure I will at least have customers.

Here’s the plan:

  • Create a page
    • Healthy/Medical/Urgent Care oriented
    • Copy is about tailored first aid kits
    • Refills first
      • Based on where you are located
        • Zip Code -> Supplies
    • Call to Action
      • Sign up for updates
  • Google Ads
    • Nationwide
  • Analytics
    • Kachug
  • 30 day trial to see if anyone is interested

I have one hour. Let’s see how far I get.

I have this site on WPEngine. So, I’ll fire up another Site. $20 per month. That’s a reasonable bet.

Bravo Med Refill Bundles. Bramedic.com. Be Brave – we’ve got you covered.

I got the domain registered. The next morning I created the DNS records to direct the domain to the WordPress site.

Stopping for a quick maintenance across sites, and to look at references for effective landing pages.

Adventures in Ubuntu Upgrades, 2023

I recently caught back up with system maintenance, part of which involved upgrading from an archaic version of Ubuntu to the latest Long Term Support (LTS). While Ubuntu isn’t perfect, it has been tolerable as various distributions have come and gone in reliability.

I found this post to follow along. They had me at “MAKE A BACKUP”:

https://www.cyberciti.biz/faq/upgrade-ubuntu-20-04-lts-to-22-04-lts/

Starting point:

  • Linux 5.4.0-163-generic x86_64
  • Description: Ubuntu 20.04.6 LTS
  • Deja Dup Backups from this morning

The process rolled along from about 10 am to.

It’s interactive. I was prompted to agree to a few service restarts. Conflict in configuration for pulse audio.

After the reboot, the outcome I feared would happen, happened. The window manager was trashed. It basically disappeared the moment the desktop was populated. The system was unusable as a desktop.

I switched to another console, and methodically removed Unity from it, after looking up the missing “dock”. After removing Unity and rebooting the system recovered with gnome and is better that this morning when I started.

So, a happy ending after all.

Time for another backup.