Mycodo Setup Guide
May 25, 2026 · View on GitHub
This guide walks through configuring Mycodo on your Raspberry Pi so that the Mycodo Hermes Skill can read sensors and control actuators via the API. It assumes Mycodo is already installed and accessible via its web dashboard.
If you haven't installed Mycodo yet, follow the official instructions at github.com/kizniche/Mycodo.
1. Enable the Mycodo API
The skill communicates with Mycodo through its built-in REST API.
- Open the Mycodo web dashboard (usually
https://<your-pi-ip>— Mycodo uses a self-signed HTTPS certificate by default) - Navigate to [Gear icon] > Configure > General
- Under API, ensure the API is enabled (it is enabled by default in Mycodo 8.15+)
- Go to [Gear icon] > Configure > Users and create or select a user with API access
- Navigate to [Gear icon] > Configure > API Keys
- Generate a new API key and copy it — this is your
MYCODO_API_KEY
Security note: The API key grants full control over your Mycodo instance. Treat it like a root password. Store it only in sensor-creds.env (which is .gitignore'd) and set permissions with chmod 600.
Self-signed certificate note: The skill's sensor-query.sh uses curl -k to skip certificate verification when talking to the Mycodo API. This is acceptable when the Pi is on a private network (e.g., Tailscale). If your Pi is exposed to the internet, consider configuring a proper TLS certificate.
2. Configure InfluxDB 2.x
Mycodo stores sensor history in InfluxDB. The skill queries InfluxDB directly for trend analysis, phase transition detection, and follow-up verification using Flux queries.
Important: This skill requires InfluxDB 2.x (it uses the v2 API, Flux query language, and token-based auth). Mycodo's default installer may set up InfluxDB 1.x depending on your Mycodo version. If your Mycodo instance is running InfluxDB 1.x, you have two options:
- Upgrade to InfluxDB 2.x — see the InfluxDB upgrade guide
- Install InfluxDB 2.x alongside — run it on port 8086 and configure Mycodo to write to both (advanced)
Check your current version:
influx version # Should report 2.x
If InfluxDB 2.x is already running
influx auth list # List existing tokens
influx bucket list # List existing buckets
If you need to configure InfluxDB 2.x
- Open the InfluxDB dashboard (usually
http://<your-pi-ip>:8086) - Create an organization if one doesn't exist (this is your
INFLUX_ORG) - Create a bucket for sensor data (this is your
INFLUX_BUCKET— Mycodo's default is typicallymycodo_db) - Generate an API token with read access to your sensor bucket (this is your
INFLUX_TOKEN)
Verify InfluxDB connectivity
From the machine that will run the skill:
curl -s -H "Authorization: Token YOUR_INFLUX_TOKEN" \
"http://YOUR_PI_IP:8086/api/v2/buckets" | jq '.buckets[].name'
You should see your bucket name in the output.
3. Set Up Sensor Inputs
The skill expects two sensors registered in Mycodo as inputs.
SHT45 (Temperature + Humidity)
- In the Mycodo dashboard, go to Data > Inputs
- Add a new input: select Sensirion SHT45 from the dropdown
- Set the measurement period (15 seconds is recommended — querying more often returns stale values)
- Activate the input and verify readings appear on the Live Measurements page
- Copy the Unique ID (UUID format, visible in the input settings or the URL bar when viewing the input) — this is your
SHT45_ID
The SHT45 provides four channels: temperature (°C), humidity (%), dewpoint (°C), and VPD (Pa). The skill uses channels 0 (temperature) and 1 (humidity) as the primary accuracy reference.
SCD41 (CO2 + Temperature + Humidity)
- Add another input: select Sensirion SCD41
- Set the measurement period (30 seconds is typical — SCD41 samples slower than SHT45)
- Activate and verify CO2 readings appear on the Live Measurements page
- Copy the Unique ID — this is your
SCD41_ID
The SCD41 provides five channels: CO2 (ppm), temperature (°C), humidity (%), dewpoint (°C), and VPD (Pa). The skill uses channel 0 (CO2) as the primary reference. SCD41 temperature and humidity are secondary — expect approximately ±0.3°C and ±10% delta vs the SHT45.
I2C note: Both sensors use I2C. If they don't appear in the input dropdown, verify I2C is enabled on your Pi:
sudo raspi-config # Interface Options > I2C > Enable
i2cdetect -y 1 # Should show 0x44 (SHT45) and 0x62 (SCD41)
If you see the addresses but Mycodo doesn't list the sensors, you may need to install additional Python dependencies for the sensor drivers. Check Mycodo's documentation for your version.
4. Set Up Actuator Outputs
The skill controls two relay-driven actuators through Mycodo outputs.
Fan (Fresh Air Exchange)
- Go to Function > Outputs
- Add a new output: select On/Off (GPIO)
- Set the GPIO pin (GPIO17 is the default in this skill's configs)
- Set
on_stateto1(active-HIGH) - Activate and test: toggle the output manually from the Mycodo dashboard to confirm the fan responds
- Copy the Unique ID — this is your
FAE_FAN_ID
Humidifier
- Add another output: On/Off (GPIO)
- Set the GPIO pin (GPIO5 is the default)
- Set
on_stateto1(active-HIGH) - Activate and test manually
- Copy the Unique ID — this is your
HUMIDIFIER_RELAY_ID
Relay wiring note: The GPIO pin controls a relay module (e.g., a DLI IoT Power Relay), which in turn switches the humidifier's mains power. The relay sits between the Pi and the humidifier — the Pi never drives the humidifier directly. Use normally-open (NO) contacts so the humidifier defaults to OFF if the Pi loses power.
Duration support: The skill uses Mycodo's built-in duration commands for timed actuator bursts (e.g., fan_burst 60 runs the fan for 60 seconds, then Mycodo automatically turns it off). This is a critical safety feature — if the skill crashes or the agent stops, Mycodo handles the auto-off. Always prefer fan_burst / humidifier_burst over indefinite fan_on / humidifier_on.
Mycodo restart warning: Restarting the Mycodo daemon resets ALL output states — both fan and humidifier will turn OFF. The decision engine will re-assert the correct state on its next cycle (up to 30 minutes later). If conditions are critical, manually re-enable the appropriate actuators after a Mycodo restart.
5. Camera (Optional)
If you want visual monitoring with embedded snapshots in reports, you need a USB webcam serving frames via HTTP on the Pi. The skill does not use Mycodo's built-in camera function — it downloads frames from a simple nginx-served endpoint.
Setup
- Install fswebcam (captures frames from USB webcams):
sudo apt install fswebcam
- Create a capture script (e.g.,
/usr/local/bin/capture-webcam.sh):
#!/bin/bash
fswebcam -r 1920x1080 --no-banner -q /var/www/html/camera/latest.jpg
- Set up a systemd timer to capture frames periodically:
# /etc/systemd/system/webcam-capture.service
[Unit]
Description=Capture webcam frame
[Service]
ExecStart=/usr/local/bin/capture-webcam.sh
User=root
# /etc/systemd/system/webcam-capture.timer
[Unit]
Description=Capture webcam frame every 5 minutes
[Timer]
OnBootSec=60
OnUnitActiveSec=5min
[Install]
WantedBy=timers.target
Enable it:
sudo systemctl enable --now webcam-capture.timer
- Serve the image via nginx:
sudo apt install nginx
Create /etc/nginx/sites-available/camera:
server {
listen 8080;
root /var/www/html/camera;
location / {
try_files $uri =404;
}
}
Enable it:
sudo ln -s /etc/nginx/sites-available/camera /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
- Verify by opening
http://<your-pi-ip>:8080/latest.jpgin a browser.
How the skill uses it
sensor-query.sh camera downloads the latest frame from http://${PI_HOST}:8080/latest.jpg via curl. When --camera is passed to the decision engine, the JPEG is base64-encoded and embedded inline in the HTML report. A raw .jpg copy is also saved alongside the HTML for contamination detection training data.
No authentication is needed — the camera endpoint relies on network-level security (e.g., Tailscale or a private LAN). Do not expose port 8080 to the public internet.
6. Fill In Your Credentials File
Copy the template and fill in the values you collected above:
mkdir -p ~/.mycodo
cp docs/templates/sensor-creds.env.example ~/.mycodo/sensor-creds.env
chmod 600 ~/.mycodo/sensor-creds.env
Edit ~/.mycodo/sensor-creds.env:
PI_HOST="192.168.1.xx" # Your Pi's IP or Tailscale hostname
MYCODO_API_KEY="your-api-key" # From step 1
INFLUX_TOKEN="your-influx-token" # From step 2
INFLUX_ORG="your-org-name" # From step 2
INFLUX_BUCKET="mycodo_db" # From step 2
SHT45_ID="xxxxxxxx-xxxx-..." # From step 3
SCD41_ID="xxxxxxxx-xxxx-..." # From step 3
FAE_FAN_ID="xxxxxxxx-xxxx-..." # From step 4
HUMIDIFIER_RELAY_ID="xxxxxxxx-xxxx-..." # From step 4
Credential path note: The sensor-query.sh script must be configured to source this file. Open scripts/sensor-query.sh and verify the CREDS_FILE variable on line 8 points to ~/.mycodo/sensor-creds.env (or wherever you placed the file). If you're running this as a Hermes skill, you may prefer a profile-specific path like ~/.hermes/profiles/<name>/credentials/sensor-creds.env.
7. Verify the Full Stack
Run these commands from the machine that will host the Hermes agent:
# Test sensor connectivity (should print temperature, humidity, CO2)
bash scripts/sensor-query.sh quick
# Test a full snapshot (all 9 sensor channels)
bash scripts/sensor-query.sh snapshot
# Test camera (optional — downloads latest frame)
bash scripts/sensor-query.sh camera /tmp/test-frame.jpg
# Test fan relay (timed burst — auto-off after 5 seconds)
bash scripts/sensor-query.sh fan_burst 5
# Test a dry-run decision (no relays fired)
python3 mycodo_skill/decision_engine.py --species lions_mane --phase fruiting
If sensor-query.sh quick returns temperature, humidity, and CO2 readings, your Mycodo and InfluxDB configuration is correct. The dry-run decision should print a report showing readings classified against Lion's Mane fruiting thresholds with a DRY RUN banner.
Troubleshooting
"Connection refused" on API calls
- Verify
PI_HOSTis reachable:ping $PI_HOST - Check Mycodo is running:
ssh pi@$PI_HOST "sudo systemctl status mycodo" - Confirm the API is enabled in Mycodo settings
"401 Unauthorized" on API calls
- Regenerate the API key in Mycodo's dashboard
- Verify the key in
sensor-creds.envmatches exactly (no trailing whitespace) - Check the file is being sourced correctly:
source ~/.mycodo/sensor-creds.env && echo $MYCODO_API_KEY
Empty credential file (0 bytes)
sensor-query.shchecks that the credential file exists (-f) but does not check that it's non-empty. A 0-byte file will silently source nothing, causing cryptic auth failures. Always verify:ls -l ~/.mycodo/sensor-creds.env— size should be > 0.
InfluxDB returns empty results
- Check the bucket name matches:
influx bucket list - Verify sensor inputs are active and logging: check Mycodo's Live Measurements page
- Confirm you're running InfluxDB 2.x, not 1.x:
influx version - InfluxDB retention policies may have expired old data
I2C sensors not detected
- Run
i2cdetect -y 1and check for device addresses (0x44 for SHT45, 0x62 for SCD41) - Verify wiring: SDA → GPIO2 (pin 3), SCL → GPIO3 (pin 5)
- Check for I2C address conflicts if using other I2C devices
Relays don't respond to commands
- Test manually from Mycodo's dashboard first (toggle the output on/off)
- Verify the Unique IDs in
sensor-creds.envmatch the outputs in Mycodo (not the inputs) - Check relay wiring: normally-open (NO) contacts are recommended
- After a Mycodo restart, all outputs reset to OFF — this is expected behavior
Camera returns "no image"
- Verify the capture service is running:
systemctl status webcam-capture.timer - Check that a fresh image exists:
ls -la /var/www/html/camera/latest.jpg - Test the HTTP endpoint directly:
curl -o /tmp/test.jpg http://localhost:8080/latest.jpg - If the image exists on the Pi but
sensor-query.sh camerafails from the agent machine, it's a network/firewall issue — check that port 8080 is accessible