← All projects

ISCInstrumentation & Signal Conditioning

LOMI Thermal Control Modification for Enhanced Composting

Reverse-engineered a commercial LOMI composter to drop its operating temperature from ~100 °C to a biologically useful ~50 °C — not by touching the firmware, but by soldering a precision parallel resistor across the NTC thermistor (CN5) to bias the analog feedback loop. Characterized empirically with a DS1922T iButton; a 19.3 kΩ resistor settled at ~49.6 °C.

  • Hardware Reverse Engineering
  • Analog Signal Conditioning
  • Thermal Dynamics
  • PCB Mod

Hardware & Architecture

  • Commercial LOMI main control board using an NTC thermistor to regulate the heating element.
  • Precision resistor soldered in parallel across the NTC terminals (connector CN5) to lower the equivalent resistance seen by the ADC.
  • DS1922T iButton data logger placed inside the chamber for independent thermal profiling.

Key Highlights

  • Parallel resistance R_eq = (R_NTC · R_p) / (R_NTC + R_p) prematurely reaches the controller's cutoff threshold, capping physical heat output.
  • Swept 12.2–19.3 kΩ: 12.2 kΩ → ~30 °C, 14.7 kΩ underdamped (overshoot past 60 °C), 15.5 kΩ → ~46 °C, 19.3 kΩ → stable ~49.6 °C.
  • Python parses DS1922T CSV exports and plots comparative thermal profiles against the 50 °C target.

Images

Code

lomi_thermal_profiles.pyiButton Parsing & Thermal Visualizationpython
import pandas as pd
import matplotlib.pyplot as plt
import glob
import os

# --- Configuration ---
# Directory containing iButton CSV exports
DATA_DIR = "./lomi_thermal_logs/"
TARGET_TEMP_C = 50.0


def load_and_clean_ibutton_data(filepath):
    """
    Parses a DS1922T iButton CSV file.
    Assumes standard iButton format: 'Date/Time' and 'Value' (Temperature).
    """
    try:
        # Skip metadata rows typically found in iButton exports
        df = pd.read_csv(filepath, skiprows=14, usecols=["Date/Time", "Value"])
        df.rename(columns={"Date/Time": "Timestamp", "Value": "Temperature_C"}, inplace=True)
        # Convert timestamp to datetime and calculate elapsed minutes for normalization
        df['Timestamp'] = pd.to_datetime(df['Timestamp'])
        start_time = df['Timestamp'].iloc[0]
        df['Elapsed_Minutes'] = (df['Timestamp'] - start_time).dt.total_seconds() / 60.0
        return df
    except Exception as e:
        print(f"[ERROR] Failed to process {filepath}: {e}")
        return None


def plot_thermal_profiles():
    """Generates a comparative plot of all tested resistor configurations."""
    plt.figure(figsize=(12, 7))
    # Define the configurations to plot and their corresponding colors
    # (Matches the visual output from lomitrail.png)
    configs = {
        "12.2k Ohm": {"file": "LOMI_12.2K.csv", "color": "#1f77b4"},
        "13.3k Ohm": {"file": "LOMI_13.3K.csv", "color": "#f2a900"},
        "14.7k Ohm": {"file": "LOMI_14.7K.csv", "color": "#009e73"},
        "15.5k Ohm": {"file": "LOMI_15.5K.csv", "color": "#d55e00"},
        "16.9k Ohm": {"file": "LOMI(16.9K)_021726.csv", "color": "#8c564b"},
    }

    for label, meta in configs.items():
        filepath = os.path.join(DATA_DIR, meta["file"])
        if os.path.exists(filepath):
            df = load_and_clean_ibutton_data(filepath)
            if df is not None:
                plt.plot(df['Elapsed_Minutes'], df['Temperature_C'],
                         label=label, color=meta["color"], linewidth=2)
        else:
            print(f"[WARNING] File not found: {filepath}")

    # Plot Target Line
    plt.axhline(y=TARGET_TEMP_C, color='r', linestyle='--', linewidth=2, label=f'Target {TARGET_TEMP_C}C')

    # Formatting
    plt.title("Comparison of LOMI Temperature Profiles via NTC Manipulation", fontsize=14, fontweight='bold')
    plt.xlabel("Time (minutes)", fontsize=12)
    plt.ylabel("Internal Temperature (C)", fontsize=12)
    plt.grid(True, linestyle='--', alpha=0.6)
    plt.legend(loc="upper right", fontsize=10)
    plt.tight_layout()
    # Save output for engineering documentation
    plt.savefig("LOMI_Thermal_Characterization.png", dpi=300)
    print("[SYSTEM] Thermal characterization plot saved successfully.")
    plt.show()


if __name__ == "__main__":
    plot_thermal_profiles()