a woman in a robe looking at a painting of a woman

Beginner’s guide to Pi3D PictureFrame’s configuration.yaml file

Pi3D PictureFrame is highly customizable but the drawback is that for many beginners, it’s quite hard to get going without getting lost in the woods.

So, here is a simple guide to which settings are important for you to get started.

You find configuration.yaml under

home/pi/picframe_data/config/configuration.yaml

There is also a configuration_example.yaml which you can revert to if you screw up your config file But in that case, don’t forget to make a copy so that you always have an untouched original.

Below, you’ll find a browser-based editing method of configuration.yaml. I am not sure if it’s really useful, but I will leave that to you. Sometimes, a man has to tinker.

Structure of the configuration.yaml file

The config file has five sections

  • viewer
  • model
  • mqtt
  • http
  • peripherals

I am not 100 percent sure if this structure has really been tidied up following many feature requests, but after some initial looking around, you can quickly find what you are looking for.

There is also an excellent wiki page with all the configuration settings, but it might be overwhelming for beginners.

So here is a distilled version meant to get you going. It is based on my personal preferences, and you can start with this and then fine-tune it from there.

What to change in the Viewer section

In this section, I change the following default settings:

  show_text: ""  # default="title caption name date folder location", show text, include combination of words: title, caption name, date, location, folder

I don’t want any text to be shown as default. I am using Home Assistant to call up the text when I want to have a particular information, but default-wise, I don’t want to see it.

  mat_type: float                          # default=null, A string containing the mat types to choose from when matting images. It can consist of any or all of 'float float_polaroid float_color_wrap single_bevel double_bevel double_flat' (null or '' will use all mat types)

I like to have the matting styles randomly selected.

What to change in the Model section

recent_n: 0                             # default=7 (days), when shuffling file change date more recent than this number of days play before the rest
  reshuffle_num: 1                        # default=1, times through before reshuffling
  time_delay: 200.0                       # default=200.0, time between consecutive slide starts - can be changed by MQTT
  fade_time: 10.0          

I set recent to 0 because the frame runs all the time, so this function has no use for me. Reshuffle is “1” because I want a fresh shuffle at the end of a playlist.

Time delay and fade time are super important. Please note that the actual fade time depends on your Pi model. Ten seconds on a Pi 5 is shorter than on a Pi 3. Turns out, time is relative after all!

Play around with the value as you like it. I prefer a long fading time, up to 30 seconds. because it sometimes creates beautiful compositions by layering two images.

  load_geoloc: True                      # get location information from open street map NB if you switch this on (recommended)
  geo_key: "abcxyz22@gmail.com"     # then you **MUST** change the geo_key to something unique to you
                                          # i.e. use your email address

I also turn on the geolocation. The geo key can be just about anything but by using your email address, you make sure that it’s unique.

  locale: "de_DE.UTF-8"                    # "locale -a" shows the installed locales which could used

Locale is only important for the language of the geolocation. Please note that you have to install a language in sudo raspi-config first, before you can use it here.

That’s really enough to get going. The other three sections will be dealt with later if you want to control your digital frame via other software, like Home Assistant or your iPhone with shortcuts.

Setting your parameters in a browser interface

This is the tinkering part that I mentioned at the beginning.

With two small scripts, you can access configuration.yaml in a browser window, which is easier for some to work with.

Here is what you need to do:

Create a new file in your /home/pi directory with

sudo nano config.py

Paste the below content in the file:

from flask import Flask, render_template, request, redirect, url_for
from ruamel.yaml import YAML
from ruamel.yaml.comments import CommentedMap

app = Flask(__name__)
CONFIG_FILE = '/home/pi/picframe_data/config/configuration.yaml'  # Path to your YAML configuration file
yaml = YAML()
yaml.preserve_quotes = True  # Preserve quotes if present in the YAML file

# Read YAML file
def read_config(file_path):
    with open(file_path, 'r') as file:
        return yaml.load(file)

# Write YAML file while preserving order and comments
def write_config(file_path, data):
    with open(file_path, 'w') as file:
        yaml.dump(data, file)

# Extract comments from the YAML object
def get_comments(node, key):
    if isinstance(node, CommentedMap) and key in node:
        comment = node.ca.items.get(key)
        if comment and comment[2]:
            return comment[2].value.strip()
    return None

# Update nested dictionaries
def update_nested_dict(original, updates):
    for key, value in updates.items():
        if isinstance(value, dict) and key in original:
            update_nested_dict(original[key], value)
        else:
            original[key] = value
    return original

@app.route('/')
def index():
    config = read_config(CONFIG_FILE)

    # Wrap non-dictionary sections in a dictionary to ensure consistency in the template
    for key, value in config.items():
        if not isinstance(value, dict):
            config[key] = {"value": value}

    # Pass configuration and comments to the template
    return render_template('index.html', config=config, get_comments=get_comments)

@app.route('/update', methods=['POST'])
def update():
    form_data = request.form.to_dict(flat=False)  # Supports nested data
    config = read_config(CONFIG_FILE)

    # Flatten form data for nested updates
    updates = {}
    for key, value in form_data.items():
        keys = key.split('.')
        current = updates
        for k in keys[:-1]:
            current = current.setdefault(k, {})
        current[keys[-1]] = value[0]

    updated_config = update_nested_dict(config, updates)
    write_config(CONFIG_FILE, updated_config)
    return redirect(url_for('index'))

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

Save and close.

Install the necessary packages with:

source /home/pi/venv_picframe/bin/activate

pip install flask pyyaml ruamel.yaml

Create a new directory with

mkdir templates

Go to that directory and create a new file with

cd templates

sudo nano index.html

Paste the below content in the file:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Configuration Editor</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <style>
        /* Floating Save Button */
        .floating-save {
            position: fixed;
            bottom: 20px;
            right: 20px;
            z-index: 1000;
        }
    </style>
</head>
<body>
    <div class="container mt-5">
        <h1>Edit Configuration</h1>
        <form method="POST" action="/update">
            {% for section, settings in config.items() %}
                <div class="card mb-3">
                    <div class="card-header">
                        <h5>{{ section }}</h5>
                    </div>
                    <div class="card-body">
                        {% for key, value in settings.items() %}
                            {% if value is mapping %}
                                <div class="mb-3">
                                    <strong>{{ key }}</strong>
                                    {% for sub_key, sub_value in value.items() %}
                                        <label class="form-label">{{ sub_key }}</label>
                                        <input type="text" name="{{ section }}.{{ key }}.{{ sub_key }}" 
                                               value="{{ sub_value }}" 
                                               class="form-control">
                                    {% endfor %}
                                </div>
                            {% elif value is iterable and value is not string %}
                                <div class="mb-3">
                                    <label class="form-label">{{ key }} (List)</label>
                                    <ul>
                                        {% for item in value %}
                                            <li>{{ item }}</li>
                                        {% endfor %}
                                    </ul>
                                </div>
                            {% else %}
                                <div class="mb-3">
                                    <label class="form-label">{{ key }}</label>
                                    <input type="text" name="{{ section }}.{{ key }}" 
                                           value="{{ value }}" 
                                           class="form-control">
                                    <small class="text-muted">
                                        {% set comment = get_comments(settings, key) %}
                                        {% if comment %}
                                            {{ comment }}
                                        {% else %}
                                            No description available
                                        {% endif %}
                                    </small>
                                </div>
                            {% endif %}
                        {% endfor %}
                    </div>
                </div>
            {% endfor %}
            <button type="submit" class="btn btn-primary floating-save">Save</button>
        </form>
    </div>
</body>
</html>

Save and close. Go back to home with cd ..

Start the script with

python config.py

Open a browser window and enter (replace the IP with your Raspberry Pi’s IP address)

http://192.168.178.74:5000/

You should now see all the parameters and their descriptions. Don’t forget to hit save before you leave the tab.

a screenshot of a computer

You can, of course, install a service that will start the script at boot so it’s always ready.

Conclusion

Getting going with Pi3D PictureFrame is easy, but the sheer number of different parameters can be overwhelming initially. Take the time to play around with the various options to personalize your frame.

Pi3D PictureFrame is the best software for digital picture frames. No commercial software comes close, and it does not require a monthly subscription.

Was this article helpful?


Thank you for your support and motivation.


Scroll to Top