|  | 
|  | 1 | +if which nproc > /dev/null; then | 
|  | 2 | +    MAKEOPTS="-j$(nproc)" | 
|  | 3 | +else | 
|  | 4 | +    MAKEOPTS="-j$(sysctl -n hw.ncpu)" | 
|  | 5 | +fi | 
|  | 6 | + | 
|  | 7 | +# TODO: Could also make these opts into the build_micropython_opencv function if we care... | 
|  | 8 | +FROZEN_MODULES_DIR="$(dirname "$0")/frozen_modules" | 
|  | 9 | +FROZEN_EXAMPLES_ARCHIVE_SCRIPT="frozen_examples.py" | 
|  | 10 | +FROZEN_EXAMPLES_UNPACKED_DIR="opencv-examples" | 
|  | 11 | +PERSISTENT_FILE_FOR_UNPACK="/${FROZEN_EXAMPLES_UNPACKED_DIR}/reset_examples.txt" | 
|  | 12 | + | 
|  | 13 | +# Uses freezefs to create a frozen filesystem archive for the provided directory. | 
|  | 14 | +# See https://github.com/bixb922/freezefs for more details on freezefs | 
|  | 15 | +# Options: | 
|  | 16 | +    # $1: The directory to freeze | 
|  | 17 | +    # $2: The name that you want the frozen directory to have once unpacked on the board | 
|  | 18 | +    # $3: The output file name for the frozen archive .py file | 
|  | 19 | +function create_frozen_fs { | 
|  | 20 | +    local DIR_TO_FREEZE=$1 | 
|  | 21 | +    local DIR_NAME_ON_BOARD=$2 | 
|  | 22 | +    local OUTPUT_FILE=$3 | 
|  | 23 | + | 
|  | 24 | +    echo "Creating frozen filesystem for directory: $DIR_TO_FREEZE" | 
|  | 25 | +    echo "The frozen directory will be named: $DIR_NAME_ON_BOARD" | 
|  | 26 | +    echo "The output file will be: $OUTPUT_FILE" | 
|  | 27 | + | 
|  | 28 | +    if [ $DIR_TO_FREEZE != $DIR_NAME_ON_BOARD ]; then | 
|  | 29 | +        cp -r $DIR_TO_FREEZE $DIR_NAME_ON_BOARD | 
|  | 30 | +    fi | 
|  | 31 | + | 
|  | 32 | +    # Use on-import=extract so our frozen filesystem is unpacked to '/' in flash on import | 
|  | 33 | +    # Use --compress to compress the frozen filesystem archive | 
|  | 34 | +    # Use --overwrite always to ensure that the frozen filesystem is returned to factory state if the persistent file is deleted | 
|  | 35 | + | 
|  | 36 | +    python -m freezefs $DIR_NAME_ON_BOARD $OUTPUT_FILE --on-import=extract --compress --overwrite always | 
|  | 37 | +} | 
|  | 38 | + | 
|  | 39 | +# Adds the provided directory to the manifest file for the specified port and board. | 
|  | 40 | +# Options: | 
|  | 41 | +# $1: The directory to add to the manifest | 
|  | 42 | +# $2: The port (e.g. rp2) | 
|  | 43 | +# $3: The board (e.g. SPARKFUN_XRP_CONTROLLER) | 
|  | 44 | +# $4: The mpconfigboard file name (e.g. mpconfigboard.cmake or mpconfigboard.m) Default: mpconfigboard.cmake | 
|  | 45 | +function add_to_manifest { | 
|  | 46 | +    local DIR=$1 | 
|  | 47 | +    local PORT=$2 | 
|  | 48 | +    local BOARD=$3 | 
|  | 49 | +    local MPCONFIG_FILE="${4:-mpconfigboard.cmake}" | 
|  | 50 | + | 
|  | 51 | +    # Add the directory to the manifest file | 
|  | 52 | +    echo "Adding $DIR to the manifest for $PORT on $BOARD using $MPCONFIG_FILE" | 
|  | 53 | +    local BOARD_DIR="micropython/ports/${PORT}/boards/${BOARD}" | 
|  | 54 | + | 
|  | 55 | +    # Create manifest.py if it doesn't exist | 
|  | 56 | +    if [ ! -f ${BOARD_DIR}/manifest.py ]; then | 
|  | 57 | +        echo "include(\"\$(PORT_DIR)/boards/manifest.py\")" > ${BOARD_DIR}/manifest.py | 
|  | 58 | + | 
|  | 59 | +        # also add the necessary frozen manifest line to mpconfigboard.cmake: set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py) | 
|  | 60 | +        # We will use the optional MPCONFIG_FILE argument to determine if we should add this line | 
|  | 61 | + | 
|  | 62 | +        if [ -n "$MPCONFIG_FILE" ]; then | 
|  | 63 | +            echo "Attempting to add frozen manifest line to $MPCONFIG_FILE for $BOARD" | 
|  | 64 | +             | 
|  | 65 | +            if [[ $MPCONFIG_FILE == *.mk ]]; then | 
|  | 66 | +                # e.g. for TEENSY which uses mpconfigboard.mk instead of mpconfigboard.cmake | 
|  | 67 | +                printf "\nFROZEN_MANIFEST ?= \$(BOARD_DIR)/manifest.py" >> ${BOARD_DIR}/$MPCONFIG_FILE | 
|  | 68 | +            elif [[ $MPCONFIG_FILE == *.cmake ]]; then | 
|  | 69 | +                printf "\nset(MICROPY_FROZEN_MANIFEST \"\${MICROPY_BOARD_DIR}/manifest.py\")" >> ${BOARD_DIR}/$MPCONFIG_FILE | 
|  | 70 | +            fi | 
|  | 71 | +        fi | 
|  | 72 | +    fi   | 
|  | 73 | + | 
|  | 74 | +    # Add the freeze line to the manifest.py for the board | 
|  | 75 | +    echo "Adding freeze line to manifest.py for $BOARD" | 
|  | 76 | +    printf "\nfreeze(\"${DIR}\")" >> ${BOARD_DIR}/manifest.py | 
|  | 77 | + | 
|  | 78 | +    # Helpful for debugging during the build process, but can be removed if we'd rather not see this output... | 
|  | 79 | +    echo "Manifest.py for $BOARD:" | 
|  | 80 | +    cat ${BOARD_DIR}/manifest.py | 
|  | 81 | +} | 
|  | 82 | + | 
|  | 83 | +# Adds the frozen data filesystem to the _boot.py file for the given port | 
|  | 84 | +# Options: | 
|  | 85 | +    # $1: Port name | 
|  | 86 | +    # $2: Frozen data file path | 
|  | 87 | +    # $3: Unpacked directory name on the board (optional). If provided, the modules in this directory will be made importable | 
|  | 88 | +function add_frozen_data_to_boot_for_port { | 
|  | 89 | +    local TARGET_PORT_NAME=$1 | 
|  | 90 | +    local FROZEN_DATA_FILE=$2 | 
|  | 91 | +    local UNPACKED_DIR=$3 | 
|  | 92 | + | 
|  | 93 | +    # Remove the ".py" extension from the frozen data file | 
|  | 94 | +    local FROZEN_DATA_BASENAME=$(basename $FROZEN_DATA_FILE .py) | 
|  | 95 | + | 
|  | 96 | +    # Check if the _boot.py file exists in the port's modules directory and error out if it does not | 
|  | 97 | +    if [ ! -f micropython/ports/${TARGET_PORT_NAME}/modules/_boot.py ]; then | 
|  | 98 | +        echo "Error: _boot.py file not found in ports/${TARGET_PORT_NAME}/modules/" | 
|  | 99 | +        exit 1 | 
|  | 100 | +    fi | 
|  | 101 | + | 
|  | 102 | +    # Add the frozen data filesystem to the _boot.py file | 
|  | 103 | +    local BOOT_FILE="micropython/ports/${TARGET_PORT_NAME}/modules/_boot.py" | 
|  | 104 | + | 
|  | 105 | +    # Create our "persistent file for unpack" that will be used to check if the frozen data filesystem has already been unpacked | 
|  | 106 | +    # If it has not been unpacked, we will import the frozen data filesystem | 
|  | 107 | +    echo "Adding frozen data filesystem to ${BOOT_FILE}" | 
|  | 108 | +    echo "import os" >> ${BOOT_FILE} | 
|  | 109 | +    echo "try:" >> ${BOOT_FILE} | 
|  | 110 | +    echo "    os.stat('${PERSISTENT_FILE_FOR_UNPACK}')" >> ${BOOT_FILE} | 
|  | 111 | +    echo "except OSError:" >> ${BOOT_FILE} | 
|  | 112 | +    echo "    import ${FROZEN_DATA_BASENAME}" >> ${BOOT_FILE} | 
|  | 113 | +    echo "    with open('${PERSISTENT_FILE_FOR_UNPACK}', 'w') as f:" >> ${BOOT_FILE} | 
|  | 114 | +    echo "        f.write('Hi! The firmware has this directory frozen into the firmware, and the _boot.py\\n')" >> ${BOOT_FILE} | 
|  | 115 | +    echo "        f.write('file has been modified to automatically unpack this directory if needed. As long\\n')" >> ${BOOT_FILE} | 
|  | 116 | +    echo "        f.write('as this file exists, it will not unpack the directory, meaning you can safely\\n')" >> ${BOOT_FILE} | 
|  | 117 | +    echo "        f.write('edit the files here or delete all other files to free up storage space. If you\\n')" >> ${BOOT_FILE} | 
|  | 118 | +    echo "        f.write('want to restore this directory to its default state, delete this file and the\\n')" >> ${BOOT_FILE} | 
|  | 119 | +    echo "        f.write('directory will be unpacked again on the next boot.\\n')" >> ${BOOT_FILE} | 
|  | 120 | +    echo "        f.write('\\n')" >> ${BOOT_FILE} | 
|  | 121 | +    echo "        f.write('WARNING: Deleting this file will override ALL changes to this directory!')" >> ${BOOT_FILE} | 
|  | 122 | + | 
|  | 123 | +    # If a destination directory is provided, we will add it to the sys.path so that the modules in the unpacked directory can be imported | 
|  | 124 | +    if [ -n "$UNPACKED_DIR" ]; then | 
|  | 125 | +        echo "Adding ${UNPACKED_DIR} to sys.path in _boot.py" | 
|  | 126 | +        echo "import sys" >> ${BOOT_FILE} | 
|  | 127 | +        echo "sys.path.append('/${UNPACKED_DIR}')" >> ${BOOT_FILE} | 
|  | 128 | +    fi | 
|  | 129 | + | 
|  | 130 | +    # Helpful for debugging during the build process, but can be removed if we'd rather not see this output... | 
|  | 131 | +    echo "Content of _boot.py after adding frozen data filesystem:" | 
|  | 132 | +    cat micropython/ports/${TARGET_PORT_NAME}/modules/_boot.py | 
|  | 133 | +} | 
|  | 134 | + | 
|  | 135 | +# Installs necessary dependencies and builds OpenCV and the firmware | 
|  | 136 | +# Also freezes the examples directory in a filesystem archive on the board | 
|  | 137 | +function build_micropython_opencv { | 
|  | 138 | +    # Install necessary packages (Could move into an install_dependencies.sh if we want this to be more explicit/modular) | 
|  | 139 | +    sudo apt update | 
|  | 140 | +    sudo apt install cmake python3 build-essential gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib | 
|  | 141 | +    # Install necessary python packages (could also move this to a requirements.txt file) | 
|  | 142 | +    pip install freezefs | 
|  | 143 | + | 
|  | 144 | +    # Create a directory for frozen modules, we can add arbitrary .py files to this directory in the future. | 
|  | 145 | +    # For now it will just contain the archived examples script. | 
|  | 146 | +    mkdir "$FROZEN_MODULES_DIR" | 
|  | 147 | + | 
|  | 148 | +    # Create our frozen filesystem archive for the examples directory | 
|  | 149 | +    create_frozen_fs "opencv-examples" "$FROZEN_EXAMPLES_UNPACKED_DIR" "$FROZEN_MODULES_DIR/$FROZEN_EXAMPLES_ARCHIVE_SCRIPT" | 
|  | 150 | + | 
|  | 151 | +    # Add necessary content to the manifest file to freeze the modules in the provided directory | 
|  | 152 | +    add_to_manifest "$FROZEN_MODULES_DIR" "rp2" "SPARKFUN_XRP_CONTROLLER" "mpconfigvariant_LARGE_BINARY.cmake" | 
|  | 153 | + | 
|  | 154 | +    # Add necessary content to the boot.py file to unpack the frozen data filesystem on boot | 
|  | 155 | +    # Provide the source and destination directories to copy the frozen data filesystem to a mutable (and non-hidden) location | 
|  | 156 | +    # Provide "true" as the last argument to add the destination directory to sys.path (since our examples directory contains modules that we want to be importable...) | 
|  | 157 | +    # add_frozen_data_to_boot_for_port "rp2" "$FROZEN_EXAMPLES_ARCHIVE_SCRIPT" ".$FROZEN_EXAMPLES_UNPACKED_DIR" "$FROZEN_EXAMPLES_UNPACKED_DIR" true | 
|  | 158 | +    add_frozen_data_to_boot_for_port "rp2" "$FROZEN_EXAMPLES_ARCHIVE_SCRIPT" "$FROZEN_EXAMPLES_UNPACKED_DIR" true | 
|  | 159 | + | 
|  | 160 | +    # Set Pico SDK path to $GITHUB_WORKSPACE/micropython/lib/pico-sdk if $GITHUB_WORKSPACE is set, otherwise use the current directory | 
|  | 161 | +    if [ -n "$GITHUB_WORKSPACE" ]; then | 
|  | 162 | +        export PICO_SDK_PATH="$GITHUB_WORKSPACE/micropython/lib/pico-sdk" | 
|  | 163 | +    else | 
|  | 164 | +        export PICO_SDK_PATH=$(dirname "$0")/micropython/lib/pico-sdk | 
|  | 165 | +    fi | 
|  | 166 | + | 
|  | 167 | +    # Build MPY Cross compiler | 
|  | 168 | +    make -C micropython/mpy-cross | 
|  | 169 | + | 
|  | 170 | +    # Update necessary MicroPython submodules | 
|  | 171 | +    make -C micropython/ports/rp2 BOARD=SPARKFUN_XRP_CONTROLLER submodules | 
|  | 172 | + | 
|  | 173 | +    # Build OpenCV | 
|  | 174 | +    make -C src/opencv PLATFORM=rp2350 --no-print-directory ${MAKEOPTS} | 
|  | 175 | + | 
|  | 176 | +    # Build firmware | 
|  | 177 | +    make BOARD=SPARKFUN_XRP_CONTROLLER ${MAKEOPTS} | 
|  | 178 | + | 
|  | 179 | +    # Rename firmware file to identify it as the OpenCV build and which board it's for | 
|  | 180 | +    mv micropython/ports/rp2/build-SPARKFUN_XRP_CONTROLLER-LARGE_BINARY/firmware.uf2 micropython/ports/rp2/build-SPARKFUN_XRP_CONTROLLER-LARGE_BINARY/MICROPYTHON_OPENCV_SPARKFUN_XRP_CONTROLLER.uf2 | 
|  | 181 | +} | 
0 commit comments