Programming languages

How to use C++ main.cpp with PlatformIO on ESP32 / esp-idf

When you are using PlatformIO to compile your firmware, it is easily possible to use a C++ main.cpp instead of the pure C main.c by just renaming main.c to main.cpp

However, you also need to properly declary app_main(). After the #include section of your main.cpp (or at the top of the file if you don’t have an #include section yet), add this code:

extern "C" {
    void app_main(void);
}

This will tell the compiler that app_main() is a C function, not a C++ function.

Full main.cpp example

#include <cstdio>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>

extern "C" {
    void app_main(void);
}

void app_main() {
    while(true) {
        printf("Hello PlatformIO!\n");
        // Wait for one second
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

 

Posted by Uli Köhler in C/C++, ESP8266/ESP32, PlatformIO

Trimming Down Long Directory and File Names using Python

This Python script takes a directory path and then proceeds to scan every file and sub-directory within, looking for any filenames or directory names that exceed a specified length. Once found, the script truncates these names to fit within the desired length and appends an ellipsis (e.g., ...) to indicate the truncation. The beauty of this script lies in its configurability and the safety features embedded within it.

Note: Using this script will lose information (because the new filenames will be shorter and any important information in the rest of the filename will be lost forever). Additionally, it might lose information in other ways. Even though we have tested it carefully, it might still destroy your data in unexpected ways. Use a

How to use

The script provides the following command line options:

usage: FixLongFilenames.py [-h] [-n LENGTH] [--ellipsis ELLIPSIS] [--dry] directory

Shorten long filenames and directory names.

positional arguments:
  directory             The directory to process.

options:
  -h, --help            show this help message and exit
  -n LENGTH, --length LENGTH
                        The maximum allowable length for directory or file names.
  --ellipsis ELLIPSIS   The ellipsis to use when shortening.
  --dry                 Dry run mode, only log what would be renamed without actual renaming.

Source code

#!/usr/bin/env python3
import os
import argparse

def shorten_path(path, max_length, ellipsis, dry_run):
    if os.path.isdir(path):
        base_name = os.path.basename(path)
        if len(base_name) > max_length:
            new_name = base_name[:max_length] + ellipsis
            new_path = os.path.join(os.path.dirname(path), new_name)
            if not os.path.exists(new_path):
                if dry_run:
                    print(f"[DRY RUN] Directory would be renamed: {path} -> {new_name}")
                else:
                    os.rename(path, new_path)
                    print(f"Renamed directory: {path} -> {new_name}")
                return new_path
    else:
        base_name, ext = os.path.splitext(os.path.basename(path))
        if len(base_name) > max_length:
            new_name = base_name[:max_length] + ellipsis + ext
            new_path = os.path.join(os.path.dirname(path), new_name)
            if not os.path.exists(new_path):
                if dry_run:
                    print(f"[DRY RUN] File would be renamed: {path} -> {new_name}")
                else:
                    os.rename(path, new_path)
                    print(f"Renamed file: {path} -> {new_name}")
                return new_path
    return path

def iterate_and_shorten(directory, max_length, ellipsis, dry_run):
    for root, dirs, files in os.walk(directory, topdown=False):
        for dname in dirs:
            dpath = os.path.join(root, dname)
            shortened_path = shorten_path(dpath, max_length, ellipsis, dry_run)
            dirs[dirs.index(dname)] = os.path.basename(shortened_path)

        for fname in files:
            fpath = os.path.join(root, fname)
            shorten_path(fpath, max_length, ellipsis, dry_run)


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Shorten long filenames and directory names.")
    parser.add_argument('directory', help="The directory to process.")
    parser.add_argument('-n', '--length', type=int, default=100, help="The maximum allowable length for directory or file names.")
    parser.add_argument('--ellipsis', default="...", help="The ellipsis to use when shortening.")
    parser.add_argument('--dry', action="store_true", help="Dry run mode, only log what would be renamed without actual renaming.")
    args = parser.parse_args()

    iterate_and_shorten(args.directory, args.length, args.ellipsis, args.dry)

 

Posted by Uli Köhler in Python

Python script to re-encode all audio files to OPUS recursively

This Python script recursively converts all FLAC, WAV and MP3 etc files recursively in the given input directory to OPUS. The default bitrate is 96k which is roughly equivalent to 192k to 256k MP3. You can select a different

After encoding, the source file and converted file are checked for their length automatically. If the length differs more than 0.2 seconds, the encoding is considered failed.

There is an option --delete to delete the source file (only if the OPUS file has equivalent length to the source file of course). Use with caution and make backups before using, no warranty is expressed or implied for this script – it might lose some of your music!

The script automatically performs parallel encoding, hence it’s pretty fast even if transcoding many files.

No files are overwritten unless the -o/--overwrite is given on the command line.

How to use

First, install ffmpeg and ffprobe. Now install the python binding for ffmpeg using

pip install ffmpeg-python

Here’s the command line options the script provides:

usage: ConvertToOPUS.py [-h] [--bitrate BITRATE] [--delete] [-o] [-j THREADS] directory

Transcode MP3, FLAC & WAV files to OPUS.

positional arguments:
  directory             Directory containing the files to transcode

options:
  -h, --help            show this help message and exit
  --bitrate BITRATE     Bitrate for the OPUS files
  --delete              Delete original files after successful transcoding
  -o, --overwrite       Overwrite existing OPUS files
  -j THREADS, --threads THREADS
                        Number of parallel transcodes

Source code

#!/usr/bin/env python3
import os
import sys
import argparse
import ffmpeg
from concurrent.futures import ProcessPoolExecutor
import logging

logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s')

def get_file_duration(file_path):
    try:
        probe = ffmpeg.probe(file_path)
        audio_stream = next((stream for stream in probe['streams'] if stream['codec_type'] == 'audio'), None)
        return float(audio_stream['duration'])
    except Exception as e:
        logging.error(f"Failed to get duration for {file_path}: {e}")
        return None

def transcode_file(file_path, bitrate, delete_after_transcode, overwrite):
    try:
        output_file = file_path.rsplit('.', 1)[0] + '.opus'
        if not overwrite and os.path.exists(output_file):
            logging.info(f"{output_file} exists. Skipping due to no overwrite flag.")
            return

        stream = (
            ffmpeg
            .input(file_path)
            .output(output_file, ab=bitrate, loglevel="error")
        )

        if overwrite:
            stream = stream.overwrite_output()

        stream.run()

        original_duration = get_file_duration(file_path)
        transcoded_duration = get_file_duration(output_file)

        if original_duration is None or transcoded_duration is None:
            logging.error(f"Failed to transcode {file_path}. Could not retrieve file duration.")
            os.remove(output_file)
            return

        duration_diff = abs(original_duration - transcoded_duration)
        if duration_diff >= 0.2:
            logging.error(f"Transcoding failed for {file_path}. Duration mismatch.")
            os.remove(output_file)
        else:
            logging.info(f"Transcoded {file_path} to {output_file} successfully.")
            if delete_after_transcode:
                os.remove(file_path)
    except Exception as e:
        logging.error(f"Failed to transcode {file_path}: {e}")

def main():
    parser = argparse.ArgumentParser(description="Transcode MP3, FLAC & WAV files to OPUS.")
    parser.add_argument("directory", type=str, help="Directory containing the files to transcode")
    parser.add_argument("--bitrate", type=str, default="96k", help="Bitrate for the OPUS files")
    parser.add_argument("--delete", action="store_true", help="Delete original files after successful transcoding")
    parser.add_argument("-o", "--overwrite", action="store_true", help="Overwrite existing OPUS files")
    parser.add_argument("-j", "--threads", type=int, default=os.cpu_count(), help="Number of parallel transcodes")

    args = parser.parse_args()

    supported_extensions = ['mp3', 'flac', 'wav', 'm4a']

    # List all files with the supported extensions in the directory recursively
    files_to_transcode = []
    for root, dirs, files in os.walk(args.directory):
        for file in files:
            if file.split('.')[-1].lower() in supported_extensions:
                files_to_transcode.append(os.path.join(root, file))

    logging.info(f"Found {len(files_to_transcode)} files to transcode.")

    with ProcessPoolExecutor(max_workers=args.threads) as executor:
        for file_path in files_to_transcode:
            executor.submit(transcode_file, file_path, args.bitrate, args.delete, args.overwrite)

if __name__ == '__main__':
    main()

 

Posted by Uli Köhler in Audio, Audio/Video, Python

Python script to remove strings from filenames and directory names recursively

This Python script renames files and directories in a given folder recursively, removing a given string from filenames and directory names. It does not require any libraries to be installed.

Note that this script has been tested, however we can’t guarantee it won’t destroy some data under some conditions. Backup your data and use with caution (best to try using --dry --verbose!)

These are the command line options it provides:

usage: RemoveFromNames.py [-h] [-c] [--dry] [-D] [-F] [-i] [-v] dir remove_string

Process and rename files and directories by removing a specific string.

positional arguments:
  dir                   Input directory to scan recursively
  remove_string         String to remove from filenames and folder names

options:
  -h, --help            show this help message and exit
  -c, --compress_spaces
                        Compress multiple spaces into one
  --dry                 Dry run. Do not actually rename anything.
  -D, --no_dirs         Don't rename directories.
  -F, --no_files        Don't rename files.
  -i, --case_insensitive
                        Perform case-insensitive compare and replace.
  -v, --verbose         Print what is being renamed.

Source code:

#!/usr/bin/env python3
import os
import argparse

def rename_files_in_directory(base_dir, remove_string, compress_spaces=False, dry_run=False, verbose=False, no_dirs=False, no_files=False, case_insensitive=False):
    if case_insensitive:
        def contains(sub, main):
            return sub.lower() in main.lower()
        
        def replace(source, target, string):
            pattern = re.compile(re.escape(source), re.IGNORECASE)
            return pattern.sub(target, string)
    else:
        contains = lambda sub, main: sub in main
        replace = lambda source, target, string: string.replace(source, target)

    # First, rename directories if not skipped by --no_dirs
    if not no_dirs:
        for dirpath, dirnames, _ in os.walk(base_dir, topdown=False):
            for dirname in dirnames:
                if contains(remove_string, dirname):
                    new_name = replace(remove_string, "", dirname)
                    if compress_spaces:
                        new_name = ' '.join(new_name.split())
                    new_name = new_name.strip()

                    if os.path.exists(os.path.join(dirpath, new_name)):
                        print(f"Cannot rename directory '{dirname}' to '{new_name}' in '{dirpath}' because a directory with the new name already exists.")
                        continue

                    if verbose:
                        print(f"Renaming directory '{dirname}' to '{new_name}' in '{dirpath}'")
                    
                    if not dry_run:
                        os.rename(os.path.join(dirpath, dirname), os.path.join(dirpath, new_name))

    # Then, rename files if not skipped by --no_files
    if not no_files:
        for dirpath, _, filenames in os.walk(base_dir):
            for filename in filenames:
                if contains(remove_string, filename):
                    new_name = replace(remove_string, "", filename)
                    if compress_spaces:
                        new_name = ' '.join(new_name.split())
                    new_name = new_name.strip()
                    
                    if os.path.exists(os.path.join(dirpath, new_name)):
                        print(f"Cannot rename '{filename}' to '{new_name}' in '{dirpath}' because a file with the new name already exists.")
                        continue
                    
                    if verbose:
                        print(f"Renaming file '{filename}' to '{new_name}' in '{dirpath}'")
                    
                    if not dry_run:
                        os.rename(os.path.join(dirpath, filename), os.path.join(dirpath, new_name))

def main():
    parser = argparse.ArgumentParser(description="Process and rename files and directories by removing a specific string.")
    
    parser.add_argument("dir", type=str, help="Input directory to scan recursively")
    parser.add_argument("remove_string", type=str, help="String to remove from filenames and folder names")
    parser.add_argument("-c", "--compress_spaces", action="store_true", help="Compress multiple spaces into one")
    parser.add_argument("--dry", action="store_true", help="Dry run. Do not actually rename anything.")
    parser.add_argument("-D", "--no_dirs", action="store_true", help="Don't rename directories.")
    parser.add_argument("-F", "--no_files", action="store_true", help="Don't rename files.")
    parser.add_argument("-i", "--case_insensitive", action="store_true", help="Perform case-insensitive compare and replace.")
    parser.add_argument("-v", "--verbose", action="store_true", help="Print what is being renamed.")

    args = parser.parse_args()

    rename_files_in_directory(args.dir, args.remove_string, args.compress_spaces, args.dry, args.verbose, args.no_dirs, args.no_files, args.case_insensitive)

if __name__ == "__main__":
    main()

 

Posted by Uli Köhler in Python

How to read multimeter and paste into Excel spreadsheet cell on hotkey press using Python

The following Python script uses PyVISA / LabInstruments to read a Rigol DM3058(E) multimeter and automatically paste the voltage reading into an open spreadsheet on Shift+Alt+Q press.

First, clone LabInstruments to the current directory using

git clone https://github.com/ulikoehler/LabInstruments.git

Now install the requirements from the following requirements.txt

system_hotkey310
pyperclip
pyvisa
pyvisa-py

and run the script:

#!/usr/bin/env python3
"""
This script allows quick reading of multimeter, automatically pasting the reading
into the current e.g. excel spreadsheet and pressing "Enter" to advance to the next cell.

Press Shift+Alt+Q to read the multimeter and paste the reading into the current cell.
"""
import subprocess
import pyautogui
import time
import re
import pyperclip
import sexpdata
import pyvisa
from LabInstruments.DM3058 import DM3058

import locale
locale.setlocale(locale.LC_NUMERIC, f"{locale.getdefaultlocale()[0]}.utf-8")

rm = pyvisa.ResourceManager()
rm.list_resources()
inst = rm.open_resource('USB0::6833::2500::DM3R245003970::0::INSTR')
multimeter = DM3058(inst)
multimeter.set_speed("S")
multimeter.mode_dc_voltage()


def run():
    voltage = multimeter.read_voltage()
    
    locale_formatted_voltage = locale.format_string("%.5f", voltage, grouping=False)
    print("Voltage: ", locale_formatted_voltage)

    # Copy locale-formatted voltage to clipboard
    pyperclip.copy(locale_formatted_voltage)
    # Paste into cell
    pyautogui.hotkey('ctrl', 'v')
    # Wait for excel
    time.sleep(0.1)
    # Press Enter key to save
    pyautogui.press('enter')

# Register hotkeys
from system_hotkey import SystemHotkey
hk = SystemHotkey()
hk.register(('shift', 'alt', 'q'), callback=lambda x: run())

# Wait for exit
while True:
    time.sleep(10)

 

Posted by Uli Köhler in Electronics, Python

How to generate inverted pulses using the ESP32 RMT module (Arduino & PlatformIO)

In our previous post ESP32 RMT pulse generation minimal example using Arduino & PlatformIO using the RMT peripheral. The pulses have a steady state (off state) of 0V and a pulse voltage of 3.3V.

If we want to generate inverted pulses, we have to invert the level entries in the pulseRMT array:

static const rmt_item32_t pulseRMT[] = {
    {{{
      /*pulse duration=*/100, /*pulse level*/0,
      // After pulse, output 1
      0, 1
    }}},
};

and additionally configure the RMT output when the pulse is finished using

config.tx_config.idle_level = RMT_IDLE_LEVEL_HIGH;
config.tx_config.idle_output_en = true;

This is how the pulse looks like:

Full example:

#include <Arduino.h>
#include <esp_log.h>
#include <driver/rmt.h>

// Output pulse train on D14
constexpr gpio_num_t rmtPin = GPIO_NUM_14;
constexpr rmt_channel_t RMT_TX_CHANNEL = RMT_CHANNEL_0;

static const rmt_item32_t pulseRMT[] = {
    {{{
      /*pulse duration=*/100, /*pulse level*/0,
      // After pulse, output 1
      0, 1
    }}},
};

void setup() {
  Serial.begin(115200);

  rmt_config_t config = RMT_DEFAULT_CONFIG_TX(rmtPin, RMT_TX_CHANNEL);
  config.clk_div = 80; // input clock 80 MHz => output clk 1 MHz
  config.tx_config.idle_level = RMT_IDLE_LEVEL_HIGH;
  config.tx_config.idle_output_en = true;

  ESP_ERROR_CHECK(rmt_config(&config));
  ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0));

}

void loop() {
  ESP_ERROR_CHECK(rmt_write_items(RMT_TX_CHANNEL, pulseRMT, sizeof(pulseRMT) / sizeof(rmt_item32_t), true));
  delay(10);
}

 

[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200

 

Posted by Uli Köhler in Arduino, C/C++, ESP8266/ESP32, PlatformIO

Jupyter ipywidgets slider making a HTTP request on change

The following Jupyter cell uses ipywidgets to display a slider from 0.0 … 1.0 and, on change, will submit a HTTP request to http://10.1.2.3.4/api/set-power?power=[slider value].

import ipywidgets as widgets
import requests
from IPython.display import display

# Define the slider widget
slider = widgets.FloatSlider(
    value=0.5,  # Initial value
    min=0.0,    # Minimum value
    max=1.0,    # Maximum value
    step=0.01,  # Step size
    description='Slider:'
)

# Define a function to handle slider changes
def on_slider_change(change):
    slider_value = change['new']
    # Define the API URL with the slider value
    api_url = f'http://10.1.2.3.4/api/set-power?power={slider_value}'
    print(api_url)
    
    # Make the HTTP request
    try:
        response = requests.get(api_url)
        response.raise_for_status()  # Raise an exception for HTTP errors
        print(f'Successfully set power to {slider_value}')
    except requests.exceptions.RequestException as e:
        print(f'Error: {e}')

# Attach the slider change handler to the slider widget
slider.observe(on_slider_change, names='value')

# Display the slider widget in the notebook
display(slider)

 

Posted by Uli Köhler in Jupyter, Python

ESP32 HTTP float query parser with range check example using HumanESPHTTP

Example for HumanESPHTTP

static const httpd_uri_t setPowerHandler = {
    .uri       = "/api/set-power",
    .method    = HTTP_GET,
    .handler   = [](httpd_req_t *request) {
        QueryURLParser parser(request);
        if(parser.HasParameter("power")) {
            std::string power = parser.GetParameter("power");
            // Parse power as float
            float powerFloat;
            try {
              powerFloat = std::stof(power);
            } catch (const std::invalid_argument& e) {
              httpd_resp_set_status(request, "400 Bad Request");
              httpd_resp_set_type(request, "text/plain");
              httpd_resp_sendstr(request, "Error: Invalid argument for power parameter (not a float)!");
              return ESP_OK;
            }
            // Check range
            if(powerFloat < 0.0 || powerFloat > 1.0) {
              httpd_resp_set_status(request, "400 Bad Request");
              httpd_resp_set_type(request, "text/plain");
              httpd_resp_sendstr(request, "Error: Invalid argument for power parameter (not in range 0.0 ... 1.0)!");
              return ESP_OK;
            }
            // TODO: Your code goes here
            // Example code: send back power
            httpd_resp_send_chunk(request, "Power is: ", HTTPD_RESP_USE_STRLEN);
            httpd_resp_send_chunk(request, std::to_string(powerFloat).c_str(), HTTPD_RESP_USE_STRLEN);
            httpd_resp_send_chunk(request, nullptr, 0); // Finished
        } else {
            httpd_resp_set_type(request, "text/plain");
              httpd_resp_set_status(request, "400 Bad Request");
            httpd_resp_sendstr(request, "No 'power' query parameter found!");
        }
        return ESP_OK;
    }
};

 

Posted by Uli Köhler in C/C++, ESP8266/ESP32

ESP32 RMT pulse generation minimal example using Arduino & PlatformIO

The following example will generate 100us pulses every 10ms. The pulses are generated using the RMT peripheral.

#include <Arduino.h>
#include <esp_log.h>
#include <driver/rmt.h>

// Output pulse train on D14
constexpr gpio_num_t rmtPin = GPIO_NUM_14;
constexpr rmt_channel_t RMT_TX_CHANNEL = RMT_CHANNEL_0;


static const rmt_item32_t pulseRMT[] = {
    {{{
      /*pulse duration=*/100, /*pulse level*/1,
      // After pulse, output 0
      0, 0
    }}},
};


void setup() {
  Serial.begin(115200);

  rmt_config_t config = RMT_DEFAULT_CONFIG_TX(rmtPin, RMT_TX_CHANNEL);
  config.clk_div = 80; // input clock 80 MHz => output clk 1 MHz

  ESP_ERROR_CHECK(rmt_config(&config));
  ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0));

}

void loop() {
  ESP_ERROR_CHECK(rmt_write_items(RMT_TX_CHANNEL, pulseRMT, sizeof(pulseRMT) / sizeof(rmt_item32_t), true));
  delay(10);
}

 

[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200

 

Posted by Uli Köhler in Arduino, C/C++, ESP8266/ESP32, PlatformIO

How to prevent pyinvoke “UnexpectedExit: Encountered a bad command exit code!”

Problem:

When you try running your command using pyinvoke’s invoke.run(), the native command returns a non-zero exit code making your script fail instead of just continuing (e.g. to evaluate the output):

Traceback (most recent call last):
File "/home/user/myscript.py", line 7, in <module>
result = invoke.run("mycommand", out_stream=outStream)
# ...
File "/usr/local/lib/python3.10/dist-packages/invoke/runners.py", line 518, in _finish
raise UnexpectedExit(result)
invoke.exceptions.UnexpectedExit: Encountered a bad command exit code!

Solution:

Use warn=True as an argument to invoke.run()

This will tell invoke to just warn you about the command exiting with non-zero exit status instead of throwing an UnexpectedExit

Posted by Uli Köhler in Python

How to compute non-inverting OpAmp amplifier gain using UliEngineering in Python

In this example, we’ll use the UliEngineering library to compute the gain of a non-inverting OpAmp amplifier, given the two feedback resistor values R1 and R2

In order to install UliEngineering (a Python 3 library) run:

sudo pip3 install -U UliEngineering

We can now use noninverting_amplifier_gain() from the UliEngineering.Electronics.OpAmp package to convert between the units of temperature:

Tip: You can pass both numbers (like 100e3) or strings (such as 100 kΩ) to most UliEngineering functions. SI prefixes like k and m are automatically decoded.

Example:

from UliEngineering.Electronics.OpAmp import noninverting_amplifier_gain

# Gain of a non-inverting amplifier with 100k & 10k feedback resistor
gain = noninverting_amplifier_gain(100e3, 10e3)
# gain = 11.0

# ... or you can use strings
gain = noninverting_amplifier_gain("100k", "10k")
 
# ... or strings with units
gain = noninverting_amplifier_gain("100kΩ", "10kΩ")

# You can also automatically format the result
from UliEngineering.EngineerIO import auto_format
print(auto_format(noninverting_amplifier_gain, "100k", "10k"))
# prints 11.0 V/V
Posted by Uli Köhler in Electronics, Python

How to access RouterOS API in Python using the ‘routeros’ library (minimal example)

This example uses the routeros library from PyPI (GitHub) to access the MikroTik API and extract the system identity.

#!/usr/bin/env python3
from routeros import login

routeros = login('admin', 'abc123abc', '192.168.88.1')

output = routeros('/system/identity/print')
# Extract the one identity string from the list of dictionaries
print(output[0]['name'])

 

Posted by Uli Köhler in MikroTik, Python

Netmiko MikroTik RouterOS SSH key login example

In our previous example Netmiko MikroTik RouterOS minimal example we showed how to login to a RouterOS device using netmikoand password-based login.

#!/usr/bin/env python3  
from netmiko import ConnectHandler
import os.path
mikrotik = {
    'device_type': 'mikrotik_routeros',
    'host':   '192.168.88.1',
    'username': 'admin',
    'key_file': os.path.expanduser("~/.ssh/id_mikrotik"),
}
with ConnectHandler(**mikrotik) as mikrotik_connection:
    print(mikrotik_connection.send_command(f'/system/identity/print', cmd_verify=False))

 

Example output:

name: MySwitch01

 

Posted by Uli Köhler in MikroTik, Networking, Python

How to pass string to executable stdin using invoke.run() in Python

In our previous example How to capture stdout to string using invoke.run() in Python we showcased how to use invoke.run(..., out_stream=...) to capture stdout to a StringIO which can then be converted to a string.

Similarly, we can pass a string to the sub-process stdin by first converting it into a StringIO and then using invoke.run(..., in_stream=...).

stdin_str = "abc123" # what we want to pass to stdint

stdin_io = StringIO(stdin_str)
result = invoke.run("myexecutable", in_stream=stdin_in)
# ...
Posted by Uli Köhler in Python

How to capture stdout to string using invoke.run() in Python

This example showcases how to capture stdout to a string instead of printing it to system stdout:

The basic trick is to initialize a StringIO (a file-like object where pyinvoke can write the stdout from the sub-process to) and pass it to the out_stream argument of invoke.run()

import invoke
from io import StringIO

outStream = StringIO()
result = invoke.run("wg genkey", out_stream=outStream)
stdout_str = outStream.getvalue()
print("Result: ", stdout_str)

 

Posted by Uli Köhler in Python

How to fix Arduino / PlatformIO undefined reference to `loop()’

Problem:

While trying to compile your Arduino or PlatformIO project, you see an error message such as
/home/uli/.platformio/packages/[email protected]+2021r2-patch5/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: .pio/build/esp32dev/libFrameworkArduino.a(main.cpp.o):(.literal._Z8loopTaskPv+0x8): undefined reference to `loop()'

Solution:

You have not declared a loop() function in your source code. Open main.cpp or your .ino source code file and start with the following (empty) loop() function which does nothing:
void loop() {
    // Nothing to do here since HTTPServer
    // is running in a separate thread
    delay(1000);
}
After you’ve added any void loop() { /* ... */} function to your sourcecode try to build/upload again and the error message should have disappeared.
If you want, you can also add code such as to print a message to the serial port every time the loop is run:
void loop() {
    // Nothing to do here since HTTPServer
    // is running in a separate thread
    Serial.println("Hello world!");
    delay(1000);
}

 

Posted by Uli Köhler in Arduino, C/C++, PlatformIO

Dynamic short pulse generator using Arduino Mega2560 with serial control

This is a simplified version of our hybrid static/dynamic pulse generator. On the Arduino Mega2560 platform, it allows generating pulses from 750ns up to almost arbitrary lengths (limited only by the integer width) with a resolution of approx. 437.5ns

In order to control the pulse width, open a serial interface and type + to increase the pulse width or - to decrease the pulse width.

#include <Arduino.h>
#include <avr/io.h>
#include <ArduinoJson.h>

const int pulseAPin = 11;

#define PORT11 PORTB
#define PIN11 5
#define PIN11_MASK (1 << PIN11)

int pulseLength = 0;

/**
 * Pulse the output pin, for very short pulses.
 */
void PulseDynamic(int n) {
    cli();
    PORT11 |= PIN11_MASK;
    // Dynamic for loop
    for (;n >= 0; n--) {
        _NOP();
    }
    PORT11 &= ~PIN11_MASK;
    sei();
}

void setup()
{
    Serial.begin(115200);
    Serial.setTimeout(25);

    pinMode(pulseAPin, OUTPUT);
    pinMode(LED_BUILTIN, OUTPUT);
}

void loop()
{
    PulseDynamic(pulseLength);
    // Process serial
    while(Serial.available() > 0) {
        int c = Serial.read();
        if(c == '+') {
            pulseLength++;
            Serial.println(pulseLength);
        } else if(c == '-') {
            pulseLength--;
            if(pulseLength < 0) {pulseLength = 0;}
            Serial.println(pulseLength);
        }
    }
    delay(50);
}

 

Posted by Uli Köhler in Arduino, C/C++

Hybrid static/dynamic Arduino Mega2560 single channel short pulse generator

This pulse generator firmware uses methods discussed e.g. in Short pulse generation with Arduino Uno Part 4: NOP for loops to generate both dynamic (for loops of NOP instructions) and static (template-based automatically unrolled) in a combined manner so you can select more or less any pulse width using a high resolution.

It doesn’t work 100% since there are still some pulse width jumps. However, it is still useful for many microsecond 5V pulse generation applications and might also be a useful technology demonstrator for hybrid static/dynamic pulsers using optimized integer template loop unrolling approaches.

In order to control the pulse width, open a serial interface and type + to increase the pulse width or - to decrease the pulse width.

This might work on Arduino Unos as well but my Uno had a broken serial interface chip so I used a ATMega2560 board.

// License: CC0 1.0 Universal
// By Uli Köhler (techoverflow.net)
#include <Arduino.h>
#include <avr/io.h>
#include <ArduinoJson.h>

#define PORT13 PORTB
#define PIN13 7
#define PIN13_MASK (1 << PIN13)

#define PORT11 PORTB
#define PIN11 5
#define PIN11_MASK (1 << PIN11)

int pulseLength = 1;

using PulseFunc = void (*)(int);

// Function pointer to void(int)
PulseFunc pulser = nullptr;
int pulserParam = 0; // Pre-computed parameter for the pulser function

void DoNothingPulser(int _) {
}

/**
 * Pulse the output pin, for very short pulses.
 * Will ONLY perform static (optimized) NOPs.
 * Will NOT perform dynamic (slower) NOP cycles
 * 
 * Hence, this function does not have any overhead from for loops.
 */
template<int nNOPs>
void PulseStatic(int _) {
    cli();
    PORT11 |= PIN11_MASK;
    // Static for loop (optimized out - template-driven)
    // Force unrolling of the loop
    // NOTE: Compiler will only unroll for n < 8
    for (int i = 0; i < min(nNOPs, 6); i++) {
        _NOP();
    }
    if(nNOPs > 6) {
        for (int i = 0; i < nNOPs - 6; i++) {
            _NOP();
        }
    }
    PORT11 &= ~PIN11_MASK;
    sei();
}

/**
 * Pulse the output pin, for very short pulses.
 * Will perform static (optimized) NOPs and
 * also dynamic (slower) NOP cycles
 */
template<int nNOPs>
void PulseDynamic(int dynamicNOPs) {
    cli();
    PORT11 |= PIN11_MASK;
    // Dynamic for loop (NOT optimized out - template-driven)
    for (int i = 0; i < dynamicNOPs; i++)
    {
        _NOP();
    }
    // Static for loop (optimized out - template-driven)
    // Force unrolling of the loop
    // NOTE: Compiler will only unroll for n < 8
    for (int i = 0; i < min(nNOPs, 6); i++) {
        _NOP();
    }
    if(nNOPs > 6) {
        for (int i = 0; i < nNOPs - 6; i++) {
            _NOP();
        }
    }
    PORT11 &= ~PIN11_MASK;
    sei();
}

PulseFunc staticPulsers[] = {
    DoNothingPulser,
    &PulseStatic<0>, // 0 NOPs
    &PulseStatic<1>, // 1 NOPs ...
    &PulseStatic<2>,
    &PulseStatic<3>,
    &PulseStatic<4>,
    &PulseStatic<5>,
    &PulseStatic<6>,
    &PulseStatic<7>,
    &PulseStatic<8>,
    &PulseStatic<9>,
    &PulseStatic<10>,
    &PulseStatic<11>
};

PulseFunc dynamicPulsers[] = {
    &PulseDynamic<0>,
    &PulseDynamic<1>,
    &PulseDynamic<2>,
    &PulseDynamic<3>,
    &PulseDynamic<4>,
    &PulseDynamic<5>,
    &PulseDynamic<6>,
    &PulseDynamic<7>,
    &PulseDynamic<8>,
    &PulseDynamic<9>,
    &PulseDynamic<10>,
    &PulseDynamic<11>
};

constexpr int A = 7;
constexpr int B = 6;

void ReconfigurePulse() {
    // Very short pulses are performed using only static
    if(pulseLength < A) {
        pulser = staticPulsers[pulseLength];
    } else {
        pulser = dynamicPulsers[(pulseLength - A) % B];
        pulserParam = (pulseLength - A) / B;
    }
}

void setup()
{
    Serial.begin(115200);
    Serial.setTimeout(25);

    ReconfigurePulse(); // with default pulseLength

    pinMode(11, OUTPUT);
    pinMode(LED_BUILTIN, OUTPUT);
}

void loop()
{
    pulser(pulserParam);
    Serial.println(pulseLength);
    //ProcessSerialInput();
    // Wait until 50ms has passed since start
    while(Serial.available() > 0) {
        int c = Serial.read();
        if(c == '+') {
            pulseLength++;
            ReconfigurePulse();
        } else if(c == '-') {
            pulseLength--;
            if(pulseLength < 0) {pulseLength = 0;}
            ReconfigurePulse();
        }
    }
    delay(50);
}

 

 

Posted by Uli Köhler in Arduino, C/C++

How to list all C++ header files included by a source or header file recursively

You can use g++ -M to list all files included by a header file recursively:

gcc -M myheader.hpp

Often you want to ignore system header files from /usr/include etc – in that case, use -MM:

gcc -MM myheader.hpp

For example:

gcc -M /usr/include/boost/circular_buffer/base.hpp

will print:

base.o: /usr/include/boost/circular_buffer/base.hpp \
 /usr/include/stdc-predef.h /usr/include/boost/config.hpp \
 /usr/include/boost/config/user.hpp \
 /usr/include/boost/config/detail/select_compiler_config.hpp \
 /usr/include/boost/config/compiler/gcc.hpp /usr/include/c++/11/cstddef \
 /usr/include/x86_64-linux-gnu/c++/11/bits/c++config.h \
 /usr/include/x86_64-linux-gnu/c++/11/bits/os_defines.h \
 /usr/include/features.h /usr/include/features-time64.h \
 /usr/include/x86_64-linux-gnu/bits/wordsize.h \
 /usr/include/x86_64-linux-gnu/bits/timesize.h \
 /usr/include/x86_64-linux-gnu/sys/cdefs.h \
 /usr/include/x86_64-linux-gnu/bits/long-double.h \
 /usr/include/x86_64-linux-gnu/gnu/stubs.h \
 /usr/include/x86_64-linux-gnu/gnu/stubs-64.h \
 /usr/include/x86_64-linux-gnu/c++/11/bits/cpu_defines.h \
 /usr/include/c++/11/pstl/pstl_config.h \
 /usr/lib/gcc/x86_64-linux-gnu/11/include/stddef.h \
 /usr/include/boost/config/detail/select_stdlib_config.hpp \
 /usr/include/c++/11/version \
 /usr/include/boost/config/stdlib/libstdcpp3.hpp /usr/include/unistd.h \
 /usr/include/x86_64-linux-gnu/bits/posix_opt.h \
 /usr/include/x86_64-linux-gnu/bits/environments.h \
 /usr/include/x86_64-linux-gnu/bits/types.h \
 /usr/include/x86_64-linux-gnu/bits/typesizes.h \
 /usr/include/x86_64-linux-gnu/bits/time64.h \
 /usr/include/x86_64-linux-gnu/bits/confname.h \
 /usr/include/x86_64-linux-gnu/bits/getopt_posix.h \
 /usr/include/x86_64-linux-gnu/bits/getopt_core.h \
 /usr/include/x86_64-linux-gnu/bits/unistd_ext.h \
 /usr/include/linux/close_range.h \
 /usr/include/boost/config/detail/select_platform_config.hpp \
 /usr/include/boost/config/platform/linux.hpp /usr/include/c++/11/cstdlib \
 /usr/include/stdlib.h \
 /usr/include/x86_64-linux-gnu/bits/libc-header-start.h \
 /usr/include/x86_64-linux-gnu/bits/waitflags.h \
 /usr/include/x86_64-linux-gnu/bits/waitstatus.h \
 /usr/include/x86_64-linux-gnu/bits/floatn.h \
 /usr/include/x86_64-linux-gnu/bits/floatn-common.h \
 /usr/include/x86_64-linux-gnu/bits/types/locale_t.h \
 /usr/include/x86_64-linux-gnu/bits/types/__locale_t.h \
 /usr/include/x86_64-linux-gnu/sys/types.h \
 /usr/include/x86_64-linux-gnu/bits/types/clock_t.h \
 /usr/include/x86_64-linux-gnu/bits/types/clockid_t.h \
 /usr/include/x86_64-linux-gnu/bits/types/time_t.h \
 /usr/include/x86_64-linux-gnu/bits/types/timer_t.h \
 /usr/include/x86_64-linux-gnu/bits/stdint-intn.h /usr/include/endian.h \
 /usr/include/x86_64-linux-gnu/bits/endian.h \
 /usr/include/x86_64-linux-gnu/bits/endianness.h \
 /usr/include/x86_64-linux-gnu/bits/byteswap.h \
 /usr/include/x86_64-linux-gnu/bits/uintn-identity.h \
 /usr/include/x86_64-linux-gnu/sys/select.h \
 /usr/include/x86_64-linux-gnu/bits/select.h \
 /usr/include/x86_64-linux-gnu/bits/types/sigset_t.h \
 /usr/include/x86_64-linux-gnu/bits/types/__sigset_t.h \
 /usr/include/x86_64-linux-gnu/bits/types/struct_timeval.h \
 /usr/include/x86_64-linux-gnu/bits/types/struct_timespec.h \
 /usr/include/x86_64-linux-gnu/bits/pthreadtypes.h \
 /usr/include/x86_64-linux-gnu/bits/thread-shared-types.h \
 /usr/include/x86_64-linux-gnu/bits/pthreadtypes-arch.h \
 /usr/include/x86_64-linux-gnu/bits/atomic_wide_counter.h \
 /usr/include/x86_64-linux-gnu/bits/struct_mutex.h \
 /usr/include/x86_64-linux-gnu/bits/struct_rwlock.h /usr/include/alloca.h \
 /usr/include/x86_64-linux-gnu/bits/stdlib-float.h \
 /usr/include/c++/11/bits/std_abs.h \
 /usr/include/boost/config/detail/posix_features.hpp \
 /usr/include/boost/config/detail/suffix.hpp \
 /usr/include/boost/config/helper_macros.hpp \
 /usr/include/boost/config/detail/cxx_composite.hpp \
 /usr/include/boost/concept_check.hpp \
 /usr/include/boost/concept/assert.hpp \
 /usr/include/boost/config/workaround.hpp \
 /usr/include/boost/concept/detail/general.hpp \
 /usr/include/boost/preprocessor/cat.hpp \
 /usr/include/boost/preprocessor/config/config.hpp \
 /usr/include/boost/concept/detail/backward_compatibility.hpp \
 /usr/include/boost/concept/detail/has_constraints.hpp \
 /usr/include/boost/type_traits/integral_constant.hpp \
 /usr/include/boost/detail/workaround.hpp \
 /usr/include/boost/type_traits/conditional.hpp \
 /usr/include/c++/11/iterator \
 /usr/include/c++/11/bits/stl_iterator_base_types.h \
 /usr/include/c++/11/type_traits \
 /usr/include/c++/11/bits/stl_iterator_base_funcs.h \
 /usr/include/c++/11/bits/concept_check.h \
 /usr/include/c++/11/debug/assertions.h \
 /usr/include/c++/11/bits/stl_iterator.h \
 /usr/include/c++/11/bits/cpp_type_traits.h \
 /usr/include/c++/11/ext/type_traits.h /usr/include/c++/11/bits/move.h \
 /usr/include/c++/11/bits/ptr_traits.h /usr/include/c++/11/iosfwd \
 /usr/include/c++/11/bits/stringfwd.h \
 /usr/include/c++/11/bits/memoryfwd.h /usr/include/c++/11/bits/postypes.h \
 /usr/include/c++/11/cwchar /usr/include/wchar.h \
 /usr/lib/gcc/x86_64-linux-gnu/11/include/stdarg.h \
 /usr/include/x86_64-linux-gnu/bits/wchar.h \
 /usr/include/x86_64-linux-gnu/bits/types/wint_t.h \
 /usr/include/x86_64-linux-gnu/bits/types/mbstate_t.h \
 /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h \
 /usr/include/x86_64-linux-gnu/bits/types/__FILE.h \
 /usr/include/x86_64-linux-gnu/bits/types/FILE.h \
 /usr/include/c++/11/bits/stream_iterator.h \
 /usr/include/c++/11/debug/debug.h \
 /usr/include/c++/11/bits/streambuf_iterator.h \
 /usr/include/c++/11/streambuf /usr/include/c++/11/bits/localefwd.h \
 /usr/include/x86_64-linux-gnu/c++/11/bits/c++locale.h \
 /usr/include/c++/11/clocale /usr/include/locale.h \
 /usr/include/x86_64-linux-gnu/bits/locale.h /usr/include/c++/11/cctype \
 /usr/include/ctype.h /usr/include/c++/11/bits/ios_base.h \
 /usr/include/c++/11/ext/atomicity.h \
 /usr/include/x86_64-linux-gnu/c++/11/bits/gthr.h \
 /usr/include/x86_64-linux-gnu/c++/11/bits/gthr-default.h \
 /usr/include/pthread.h /usr/include/sched.h \
 /usr/include/x86_64-linux-gnu/bits/sched.h \
 /usr/include/x86_64-linux-gnu/bits/types/struct_sched_param.h \
 /usr/include/x86_64-linux-gnu/bits/cpu-set.h /usr/include/time.h \
 /usr/include/x86_64-linux-gnu/bits/time.h \
 /usr/include/x86_64-linux-gnu/bits/timex.h \
 /usr/include/x86_64-linux-gnu/bits/types/struct_tm.h \
 /usr/include/x86_64-linux-gnu/bits/types/struct_itimerspec.h \
 /usr/include/x86_64-linux-gnu/bits/setjmp.h \
 /usr/include/x86_64-linux-gnu/bits/types/struct___jmp_buf_tag.h \
 /usr/include/x86_64-linux-gnu/bits/pthread_stack_min-dynamic.h \
 /usr/include/x86_64-linux-gnu/c++/11/bits/atomic_word.h \
 /usr/include/x86_64-linux-gnu/sys/single_threaded.h \
 /usr/include/c++/11/bits/locale_classes.h /usr/include/c++/11/string \
 /usr/include/c++/11/bits/char_traits.h \
 /usr/include/c++/11/bits/stl_algobase.h \
 /usr/include/c++/11/bits/functexcept.h \
 /usr/include/c++/11/bits/exception_defines.h \
 /usr/include/c++/11/ext/numeric_traits.h \
 /usr/include/c++/11/bits/stl_pair.h \
 /usr/include/c++/11/bits/predefined_ops.h /usr/include/c++/11/cstdint \
 /usr/lib/gcc/x86_64-linux-gnu/11/include/stdint.h /usr/include/stdint.h \
 /usr/include/x86_64-linux-gnu/bits/stdint-uintn.h \
 /usr/include/c++/11/bits/allocator.h \
 /usr/include/x86_64-linux-gnu/c++/11/bits/c++allocator.h \
 /usr/include/c++/11/ext/new_allocator.h /usr/include/c++/11/new \
 /usr/include/c++/11/bits/exception.h \
 /usr/include/c++/11/bits/ostream_insert.h \
 /usr/include/c++/11/bits/cxxabi_forced.h \
 /usr/include/c++/11/bits/stl_function.h \
 /usr/include/c++/11/backward/binders.h \
 /usr/include/c++/11/bits/range_access.h \
 /usr/include/c++/11/initializer_list \
 /usr/include/c++/11/bits/basic_string.h \
 /usr/include/c++/11/ext/alloc_traits.h \
 /usr/include/c++/11/bits/alloc_traits.h \
 /usr/include/c++/11/bits/stl_construct.h /usr/include/c++/11/string_view \
 /usr/include/c++/11/bits/functional_hash.h \
 /usr/include/c++/11/bits/hash_bytes.h \
 /usr/include/c++/11/bits/string_view.tcc \
 /usr/include/c++/11/ext/string_conversions.h /usr/include/c++/11/cstdio \
 /usr/include/stdio.h /usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h \
 /usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h \
 /usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h \
 /usr/include/x86_64-linux-gnu/bits/types/cookie_io_functions_t.h \
 /usr/include/x86_64-linux-gnu/bits/stdio_lim.h \
 /usr/include/c++/11/cerrno /usr/include/errno.h \
 /usr/include/x86_64-linux-gnu/bits/errno.h /usr/include/linux/errno.h \
 /usr/include/x86_64-linux-gnu/asm/errno.h \
 /usr/include/asm-generic/errno.h /usr/include/asm-generic/errno-base.h \
 /usr/include/x86_64-linux-gnu/bits/types/error_t.h \
 /usr/include/c++/11/bits/charconv.h \
 /usr/include/c++/11/bits/basic_string.tcc \
 /usr/include/c++/11/bits/locale_classes.tcc \
 /usr/include/c++/11/system_error \
 /usr/include/x86_64-linux-gnu/c++/11/bits/error_constants.h \
 /usr/include/c++/11/stdexcept /usr/include/c++/11/exception \
 /usr/include/c++/11/bits/exception_ptr.h \
 /usr/include/c++/11/bits/cxxabi_init_exception.h \
 /usr/include/c++/11/typeinfo /usr/include/c++/11/bits/nested_exception.h \
 /usr/include/c++/11/bits/streambuf.tcc \
 /usr/include/boost/type_traits/conversion_traits.hpp \
 /usr/include/boost/type_traits/is_convertible.hpp \
 /usr/include/boost/type_traits/intrinsics.hpp \
 /usr/include/boost/type_traits/detail/config.hpp \
 /usr/include/boost/version.hpp \
 /usr/include/boost/type_traits/is_complete.hpp \
 /usr/include/boost/type_traits/declval.hpp \
 /usr/include/boost/type_traits/add_rvalue_reference.hpp \
 /usr/include/boost/type_traits/is_void.hpp \
 /usr/include/boost/type_traits/is_reference.hpp \
 /usr/include/boost/type_traits/is_lvalue_reference.hpp \
 /usr/include/boost/type_traits/is_rvalue_reference.hpp \
 /usr/include/boost/type_traits/remove_reference.hpp \
 /usr/include/boost/type_traits/is_function.hpp \
 /usr/include/boost/type_traits/detail/is_function_cxx_11.hpp \
 /usr/include/boost/type_traits/detail/yes_no_type.hpp \
 /usr/include/boost/type_traits/is_array.hpp \
 /usr/include/boost/static_assert.hpp \
 /usr/include/boost/type_traits/is_arithmetic.hpp \
 /usr/include/boost/type_traits/is_integral.hpp \
 /usr/include/boost/type_traits/is_floating_point.hpp \
 /usr/include/boost/type_traits/is_abstract.hpp \
 /usr/include/boost/type_traits/add_lvalue_reference.hpp \
 /usr/include/boost/type_traits/add_reference.hpp \
 /usr/include/c++/11/utility /usr/include/c++/11/bits/stl_relops.h \
 /usr/include/boost/type_traits/is_same.hpp \
 /usr/include/boost/concept/usage.hpp \
 /usr/include/boost/concept/detail/concept_def.hpp \
 /usr/include/boost/preprocessor/seq/for_each_i.hpp \
 /usr/include/boost/preprocessor/arithmetic/dec.hpp \
 /usr/include/boost/preprocessor/config/limits.hpp \
 /usr/include/boost/preprocessor/arithmetic/limits/dec_256.hpp \
 /usr/include/boost/preprocessor/arithmetic/inc.hpp \
 /usr/include/boost/preprocessor/arithmetic/limits/inc_256.hpp \
 /usr/include/boost/preprocessor/control/if.hpp \
 /usr/include/boost/preprocessor/control/iif.hpp \
 /usr/include/boost/preprocessor/logical/bool.hpp \
 /usr/include/boost/preprocessor/logical/limits/bool_256.hpp \
 /usr/include/boost/preprocessor/repetition/for.hpp \
 /usr/include/boost/preprocessor/debug/error.hpp \
 /usr/include/boost/preprocessor/facilities/empty.hpp \
 /usr/include/boost/preprocessor/detail/auto_rec.hpp \
 /usr/include/boost/preprocessor/detail/limits/auto_rec_256.hpp \
 /usr/include/boost/preprocessor/repetition/detail/for.hpp \
 /usr/include/boost/preprocessor/control/expr_iif.hpp \
 /usr/include/boost/preprocessor/tuple/eat.hpp \
 /usr/include/boost/preprocessor/repetition/detail/limits/for_256.hpp \
 /usr/include/boost/preprocessor/repetition/limits/for_256.hpp \
 /usr/include/boost/preprocessor/seq/seq.hpp \
 /usr/include/boost/preprocessor/seq/elem.hpp \
 /usr/include/boost/preprocessor/seq/limits/elem_256.hpp \
 /usr/include/boost/preprocessor/seq/size.hpp \
 /usr/include/boost/preprocessor/seq/limits/size_256.hpp \
 /usr/include/boost/preprocessor/seq/detail/is_empty.hpp \
 /usr/include/boost/preprocessor/logical/compl.hpp \
 /usr/include/boost/preprocessor/tuple/elem.hpp \
 /usr/include/boost/preprocessor/facilities/expand.hpp \
 /usr/include/boost/preprocessor/facilities/overload.hpp \
 /usr/include/boost/preprocessor/variadic/size.hpp \
 /usr/include/boost/preprocessor/facilities/check_empty.hpp \
 /usr/include/boost/preprocessor/variadic/has_opt.hpp \
 /usr/include/boost/preprocessor/variadic/limits/size_64.hpp \
 /usr/include/boost/preprocessor/tuple/rem.hpp \
 /usr/include/boost/preprocessor/tuple/detail/is_single_return.hpp \
 /usr/include/boost/preprocessor/variadic/elem.hpp \
 /usr/include/boost/preprocessor/variadic/limits/elem_64.hpp \
 /usr/include/boost/preprocessor/seq/enum.hpp \
 /usr/include/boost/preprocessor/seq/limits/enum_256.hpp \
 /usr/include/boost/preprocessor/comma_if.hpp \
 /usr/include/boost/preprocessor/punctuation/comma_if.hpp \
 /usr/include/boost/preprocessor/punctuation/comma.hpp \
 /usr/include/boost/concept/detail/concept_undef.hpp \
 /usr/include/boost/limits.hpp /usr/include/c++/11/limits \
 /usr/include/boost/core/allocator_access.hpp \
 /usr/include/boost/core/pointer_traits.hpp \
 /usr/include/boost/core/addressof.hpp \
 /usr/include/boost/core/empty_value.hpp \
 /usr/include/boost/type_traits/is_stateless.hpp \
 /usr/include/boost/type_traits/has_trivial_constructor.hpp \
 /usr/include/boost/type_traits/is_pod.hpp \
 /usr/include/boost/type_traits/is_scalar.hpp \
 /usr/include/boost/type_traits/is_enum.hpp \
 /usr/include/boost/type_traits/is_pointer.hpp \
 /usr/include/boost/type_traits/is_member_pointer.hpp \
 /usr/include/boost/type_traits/is_member_function_pointer.hpp \
 /usr/include/boost/type_traits/detail/is_member_function_pointer_cxx_11.hpp \
 /usr/include/boost/type_traits/is_default_constructible.hpp \
 /usr/include/boost/type_traits/is_volatile.hpp \
 /usr/include/boost/type_traits/has_trivial_copy.hpp \
 /usr/include/boost/type_traits/is_copy_constructible.hpp \
 /usr/include/boost/type_traits/is_constructible.hpp \
 /usr/include/boost/type_traits/is_destructible.hpp \
 /usr/include/boost/type_traits/has_trivial_destructor.hpp \
 /usr/include/boost/type_traits/is_class.hpp \
 /usr/include/boost/type_traits/is_empty.hpp \
 /usr/include/boost/type_traits/remove_cv.hpp \
 /usr/include/boost/type_traits/is_nothrow_move_constructible.hpp \
 /usr/include/boost/type_traits/enable_if.hpp \
 /usr/include/boost/type_traits/is_nothrow_move_assignable.hpp \
 /usr/include/boost/type_traits/has_trivial_move_assign.hpp \
 /usr/include/boost/type_traits/is_assignable.hpp \
 /usr/include/boost/type_traits/has_nothrow_assign.hpp \
 /usr/include/boost/type_traits/is_const.hpp \
 /usr/include/boost/move/adl_move_swap.hpp \
 /usr/include/boost/move/detail/workaround.hpp \
 /usr/include/boost/move/utility_core.hpp \
 /usr/include/boost/move/detail/config_begin.hpp \
 /usr/include/boost/move/core.hpp \
 /usr/include/boost/move/detail/config_end.hpp \
 /usr/include/boost/move/detail/meta_utils.hpp \
 /usr/include/boost/move/detail/meta_utils_core.hpp \
 /usr/include/boost/move/detail/addressof.hpp \
 /usr/include/boost/move/move.hpp /usr/include/boost/move/utility.hpp \
 /usr/include/boost/move/traits.hpp \
 /usr/include/boost/move/detail/type_traits.hpp \
 /usr/include/c++/11/cassert /usr/include/assert.h \
 /usr/include/boost/move/iterator.hpp \
 /usr/include/boost/move/detail/iterator_traits.hpp \
 /usr/include/boost/move/detail/std_ns_begin.hpp \
 /usr/include/boost/move/detail/std_ns_end.hpp \
 /usr/include/boost/move/algorithm.hpp \
 /usr/include/boost/move/algo/move.hpp \
 /usr/include/boost/move/detail/iterator_to_raw_pointer.hpp \
 /usr/include/boost/move/detail/to_raw_pointer.hpp \
 /usr/include/boost/move/detail/pointer_element.hpp \
 /usr/include/c++/11/algorithm /usr/include/c++/11/bits/stl_algo.h \
 /usr/include/c++/11/bits/algorithmfwd.h \
 /usr/include/c++/11/bits/stl_heap.h \
 /usr/include/c++/11/bits/stl_tempbuf.h \
 /usr/include/c++/11/bits/uniform_int_dist.h \
 /usr/include/c++/11/pstl/glue_algorithm_defs.h \
 /usr/include/c++/11/functional /usr/include/c++/11/tuple \
 /usr/include/c++/11/array /usr/include/c++/11/bits/uses_allocator.h \
 /usr/include/c++/11/bits/invoke.h /usr/include/c++/11/bits/refwrap.h \
 /usr/include/c++/11/bits/std_function.h \
 /usr/include/c++/11/unordered_map \
 /usr/include/c++/11/ext/aligned_buffer.h \
 /usr/include/c++/11/bits/hashtable.h \
 /usr/include/c++/11/bits/hashtable_policy.h \
 /usr/include/c++/11/bits/enable_special_members.h \
 /usr/include/c++/11/bits/node_handle.h \
 /usr/include/c++/11/bits/unordered_map.h \
 /usr/include/c++/11/bits/erase_if.h /usr/include/c++/11/vector \
 /usr/include/c++/11/bits/stl_uninitialized.h \
 /usr/include/c++/11/bits/stl_vector.h \
 /usr/include/c++/11/bits/stl_bvector.h \
 /usr/include/c++/11/bits/vector.tcc \
 /usr/include/c++/11/pstl/execution_defs.h /usr/include/c++/11/memory \
 /usr/include/c++/11/bits/stl_raw_storage_iter.h \
 /usr/include/c++/11/bits/align.h /usr/include/c++/11/bit \
 /usr/include/c++/11/bits/unique_ptr.h \
 /usr/include/c++/11/bits/shared_ptr.h \
 /usr/include/c++/11/bits/shared_ptr_base.h \
 /usr/include/c++/11/bits/allocated_ptr.h \
 /usr/include/c++/11/ext/concurrence.h \
 /usr/include/c++/11/bits/shared_ptr_atomic.h \
 /usr/include/c++/11/bits/atomic_base.h \
 /usr/include/c++/11/bits/atomic_lockfree_defines.h \
 /usr/include/c++/11/backward/auto_ptr.h \
 /usr/include/c++/11/pstl/glue_memory_defs.h /usr/include/c++/11/deque \
 /usr/include/c++/11/bits/stl_deque.h /usr/include/c++/11/bits/deque.tcc

 

Posted by Uli Köhler in C/C++

How to list only sub-folders (not files) using Python

Whereas Python’s os.listdir("mydirectory") will list both sub-files and sub-directories, you can use this simple list comprehension:

parent_folder = "myfolder"
sub_directories = [
    filename for filename in os.listdir(parent_folder)
    if os.path.isdir(os.path.join(parent_folder, filename))
]

which will only list sub-directories of the parent_fodler

 

Posted by Uli Köhler in Python