Add files via upload

This commit is contained in:
Ulf D. 2025-06-15 13:29:52 +02:00 committed by GitHub
parent 983c93c376
commit 06930adecf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 1009 additions and 0 deletions

112
latest/README.md Normal file
View file

@ -0,0 +1,112 @@
## Anycubic Kobra 2 Series Tools
This repository contains tools for the Anycubic Kobra 2 Series 3D printers.
> [!WARNING]
> This repository is unmaintained. I'm not using the printer anymore and I don't have the time to maintain it. I'm archiving this repository and will delete it in 1 year. So if you want to use it, you need to fork it and maintain it yourself. Same goes for the firmware repository.
### Documentation
Documentation can be found in the `docs` directory.
- [VERSIONS.md](docs/VERSIONS.md) - Known firmware versions.
- [LINKS.md](docs/LINKS.md) - Useful things/addons for the printer.
- [OPTIONS.md](docs/OPTIONS.md) - Options for the firmware.
- [GCODE_COMMANDS.md](docs/GCODE_COMMANDS.md) - GCODE commands.
- [MQTT_API.md](docs/MQTT_API.md) - MQTT API.
- [COMMANDS.md](docs/COMMANDS.md) - Useful commands.
- [PRINTER_CFG.md](docs/PRINTER_CFG.md) - Printer.cfg things.
- [EMMC_BACKUP.md](docs/EMMC_BACKUP.md) - How to backup the EMMC.
- [EMMC_RESTORE.md](docs/EMMC_RESTORE.md) - How to restore the EMMC.
- [ENTER_FEL_MODE.md](docs/ENTER_FEL_MODE.md) - How to enter FEL mode.
- [DOWNLOAD_SDK.md](docs/DOWNLOAD_SDK.md) - How to download the SDK.
- [OLD_INFO.md](docs/OLD_INFO.md) - Old information.
- [CREDITS.md](docs/CREDITS.md) - Credits.
## Usage
> [!IMPORTANT]
> Please backup all files in `/user` so you don't lose access to anycubic cloud and OTA updates. You can use the [EMMC_BACKUP.md](./EMMC_BACKUP.md) guide to backup the whole system. But backing up `/user` is enough to keep access to anycubic cloud and OTA updates.
>
> You could use `dd` command to backup also.
> [!CAUTION] > **IF YOU DO NOT BACKUP OR DELETE THE FILES IN `/user` YOU WILL LOSE ACCESS TO ANYCUBIC CLOUD AND OTA UPDATES. YOU HAVE BEEN WARNED.**
>
> Everything you do is on **your** own risk. I am **not** responsible for any damage you do to your printer.
> [!IMPORTANT]
> For python scripts to work you need to have installed `python3` and `pip3` and then install the required packages with `pip3 install -r requirements.txt`.
#### 1. Clone the repository.
#### 2. Make sure you have uart cable connected and have downgraded to version `2.3.9` so you can continue with the next steps.
> [!NOTE]
> If not you may not get any uart output at all. To downgrade just put the `2.3.9` version on usb like you always do.
#### 3. Place the `.bin .zip .swu` firmware files in the `FW` directory.
> [!TIP]
> If you don't have firmware files, you can use the script `fwdl.sh <model> <version>` to download in the folder `FW` the version for the printer model you need. The supported models are `K2Pro`, `K2Plus` and `K2Max`. The version is given in the format `X.Y.Z` like `3.0.9`.
#### 4. Run `unpack.sh <update_file>` to unpack the selected firmware update file.
The supported file extensions are `bin`, `zip` and `swu`. The result is in the folder `unpacked`.
#### 5. Modify the options file `options.cfg`.
To select the options you need and run `patch.sh` to patch the firmware files in the `unpacked` folder.
The result is still in the folder `unpacked`. You may manually modify the current state of the files if needed. You can also prepare different configuration files for different needs based on the default file `options.cfg`. The custom configuration file is provided as parameter: `patch.sh <custom_configuration_file>`. If no parameter is provided, the file `options.cfg` will be used.
#### 6. Run `pack.sh` to pack the firmware files from the folder `unpacked`.
The result is the file `update/update.swu`.
At the end, if you selected `ssh` and `root_access` with a password, you will be asked if you want to upload the update automatically through ssh. If your printer has already a custom update (with ssh and root password) you can type `y` and press `enter`. The update will be transferred to the printer, executed and the printer will reboot. Otherwise, press enter to exit and follow the next step for USB update.
#### 7. If your printer is still with the original firmware, you have to make root access first.
Then replace the `/etc/swupdate_public.pem` in the printer with the one from the `RESOURCES` directory or create your own (make a copy first of the original `/etc/swupdate_public.pem` key in case you want to return to the original `ota` updates).
Then apply the newly generated custom software `update/update.swu` by USB update (place the file `update.swu` in the folder `update` on the root of a FAT32 formatted USB disk). If your printer already has custom update installed, then you can directly apply the new update by USB update.
To do all this a little easier you can just use `build.sh` and it will run all the steps for you.
> [!WARNING]
> This repository is a work in progress and may contain bugs or may not work as expected any pull requests are welcome.
> [!NOTE]
> Default password for the root access (custom firmware with UART and SSH) is `toor` but it can be changed in the `options.cfg` file.
> [!IMPORTANT]
> Start the scripts directly by `./script_name.sh <parameters>` to be started by the requested `bash` shell. Shells like `sh` are not compatible at this time.
> [!IMPORTANT]
> Use only FAT32 formatted USB disk and place the file `update.swu` inside a folder `update` created at the root of the USB disk. You don't have to have a 4 GB usb. It can be 64 or 128 GB or more. You only need to format 1 partition to max 4 GB. Then FAT32 will be available.
> [!TIP]
> In order for the auto update upload to work properly, you need to setup in advance the configuration file `auto_install.cfg`. It requires one line of text with the following information:
> `host_ip`,`user_name`,`printer_ip`,`ssh_port`
> Example:
> `192.168.1.234,root,192.168.1.242,22`
>
> Only applies if you have already rooted and installed ssh on the printer.
### Advanced usage
Partition map of the printer:
![Partition map](./docs/images/partition.jpg)
### Information
**FW** - Place `.bin`, `.zip` or `.swu` firmware files here.
**RESOURCES** - Contains resources for the firmware options.
**TOOLS** - Contains tools to decrypt and encrypt firmware files and more.
**unpacked** - Contains the unpacked firmware files.
**update** - Contains the packed firmware files.

260
latest/build.sh Normal file
View file

@ -0,0 +1,260 @@
#!/bin/bash
project_root="$PWD"
# Source the utils.sh file
source "$project_root/TOOLS/helpers/utils.sh" "$project_root"
# 0 arguments: interactive mode
# 1 argument: firmware file or configuration file
# 2 arguments: firmware file and configuration file
usage() {
echo "usage : $0 [firmware_file] [configuration_file]"
exit 1
}
# check the required tools
check_tools "awk zip app_version.sh app_model.sh ack2_swu_encrypt.py python3"
# set the custom encrypt tool
ENCRYPT_TOOL=$(which "ack2_swu_encrypt.py")
if [ -z "$ENCRYPT_TOOL" ]; then
# if not installed use the local copy
ENCRYPT_TOOL="TOOLS/ack2_swu_encrypt.py"
fi
# selected fw file
selected_firmware_file=""
# selected config file
selected_config_file="options.cfg"
# check first for a default file set by update.bin|zip|swu
default_firmware_file=""
if [ -f "$FW_DIR/update.swu" ]; then
default_firmware_file="$FW_DIR/update.swu"
elif [ -f "$FW_DIR/update.zip" ]; then
default_firmware_file="$FW_DIR/update.zip"
elif [ -f "$FW_DIR/update.bin" ]; then
default_firmware_file="$FW_DIR/update.bin"
fi
if [ $# -eq 0 ]; then
# no arguments provided
if [ -n "$default_firmware_file" ]; then
# but default file exists, use it
selected_firmware_file="$default_firmware_file"
fi
elif [ $# -eq 1 ]; then
# one argument provided
fw_file="$1"
fw_file_ext="${fw_file##*.}"
if [ "$fw_file_ext" = "swu" ] || [ "$fw_file_ext" = "bin" ] || [ "$fw_file_ext" = "zip" ]; then
if [ -f "$fw_file" ]; then
# it is a valid firmware file
selected_firmware_file="$fw_file"
elif [ -f "$FW_DIR/$fw_file" ]; then
selected_firmware_file="$FW_DIR/$fw_file"
else
usage
fi
else
cfg_file="$project_root/$1"
if [ -f "$cfg_file" ]; then
# it is a configuration file with ext
selected_config_file="$cfg_file"
elif [ -f "${cfg_file}.cfg" ]; then
echo "${cfg_file}.cfg"
# it is a configuration file without ext
selected_config_file="${cfg_file}.cfg"
else
usage
fi
selected_firmware_file="$default_firmware_file"
fi
elif [ $# -eq 2 ]; then
# two arguments provided
fw_file="$1"
fw_file_ext="${fw_file##*.}"
if [ "$fw_file_ext" = "swu" ] || [ "$fw_file_ext" = "bin" ] || [ "$fw_file_ext" = "zip" ]; then
if [ -f "$fw_file" ]; then
# it is a valid firmware file
selected_firmware_file="$fw_file"
elif [ -f "$FW_DIR/$fw_file" ]; then
selected_firmware_file="$FW_DIR/$fw_file"
else
usage
fi
else
usage
fi
cfg_file="$project_root/$2"
if [ -f "$cfg_file" ]; then
# it is a configuration file with ext
selected_config_file="$cfg_file"
elif [ -f "${cfg_file}.cfg" ]; then
# it is a configuration file without ext
selected_config_file="${cfg_file}.cfg"
else
usage
fi
elif [ $# -ge 3 ]; then
# 3 or more arguments provided
usage
fi
# check the config file for build_input and build_output options
build_input=""
build_output=""
auto_install_tool=""
if [ -f "$selected_config_file" ]; then
# parse the enabled options that have a set value
options=$(awk -F '=' '{if (! ($0 ~ /^;/) && ! ($0 ~ /^#/) && ! ($0 ~ /^$/) && ! ($2 == "")) print $1}' "$selected_config_file")
# for each enabled option
for option in $options; do
parameters=$(awk -F '=' "{if (! (substr(\$0,1,1) == \"#\") && ! (substr(\$0,1,1) == \";\") && ! (\$1 == \"\") && ! (\$2 == \"\") && (\$1 ~ /$option/ ) ) print \$2}" "$selected_config_file" | head -n 1)
# replace the project root requests
parameter="${parameters/@/"$project_root"}"
# remove the leading and ending double quotes
parameter=$(echo "$parameter" | sed -e 's/^"//' -e 's/"$//')
# remove the leading and ending single quotes
parameter=$(echo "$parameter" | sed -e 's/^'\''//' -e 's/'\''$//')
if [ "$option" = "build_input" ]; then
build_input="$parameter"
fi
if [ "$option" = "build_output" ]; then
build_output="$parameter"
fi
if [ "$option" = "auto_install" ]; then
auto_install_tool="$parameter"
fi
done
fi
if [ -z "$selected_firmware_file" ] && [ -n "$build_input" ] && [ -f "$build_input" ]; then
# no fw file provided but the config file has a valid fw file set, use that file
selected_firmware_file="$build_input"
fi
if [ -z "$selected_firmware_file" ]; then
# No firmware file selected by the user: interactive mode
# Check if firmware exists in the FW folder else ask the user to download it
# List all files in $FW_DIR and check if there are any files .zip, .bin or .swu
# If there are files, ask the user if they want to use the files in the FW folder else ask the user to download the firmware
# Get all files in the FW folder that are .zip, .bin or .swu
all_files=$(ls $FW_DIR | grep -E ".zip|.bin|.swu")
# If there are no files in the FW folder, ask the user to download the firmware
if [ -z "$all_files" ]; then
read -p "No firmware files found in the FW folder. Do you want to download the firmware? (y/n) " -n 1 -r
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo
read -p "Enter version: " par_version
read -p "Enter model: " par_model
echo "Downloading firmware..."
# Run fwdl.sh with the model and version as parameters
$project_root/fwdl.sh $par_model $par_version
all_files=$(ls $FW_DIR | grep -E ".zip|.bin|.swu")
else
echo
echo "Please download the firmware and place it in the FW folder"
exit 2
fi
fi
# If there are files in the FW folder, ask the user if they want to use the files in the FW folder
if [ -n "$all_files" ]; then
# List all firmwares and ask user to pick an available firmware version
echo "Available firmware versions:"
for file in $all_files; do
echo $file
done
read -p "Which file do you want to use? " firmware_file
if [ -f "$FW_DIR/$firmware_file" ]; then
echo "Using firmware $firmware_file"
else
echo "Firmware file not found"
exit 3
fi
fi
selected_firmware_file="$FW_DIR/$firmware_file"
fi
# Unpack the firmware
echo "Unpacking firmware..."
"$project_root/unpack.sh" "$selected_firmware_file"
if [ $? -ne 0 ]; then
echo "Failed to unpack firmware"
exit 4
fi
# Patch the firmware
echo "Patching firmware..."
"$project_root/patch.sh" "$selected_config_file"
if [ $? -ne 0 ]; then
echo "Failed to patch firmware"
exit 5
fi
# Build the firmware
echo "Building firmware..."
"$project_root/pack.sh"
if [ $? -ne 0 ]; then
echo "Failed to build firmware"
exit 6
fi
# Process the output file if set
if [ -n "$build_output" ]; then
# try to find out the app version (like app_ver="3.1.0")
def_target="$ROOTFS_DIR/app/app"
app_ver=$("$app_version_tool" "$def_target")
if [ $? != 0 ]; then
echo -e "${RED}ERROR: Cannot find the app version ${NC}"
exit 4
fi
# try to find out the model
app_model=$("$app_model_tool" "$def_target")
if [ $? != 0 ]; then
echo -e "${RED}ERROR: Cannot find the app model ${NC}"
exit 5
fi
rm -f "$project_root/update.bin"
rm -f "$project_root/update.zip"
zip -r "$project_root/update.zip" update
$ENCRYPT_TOOL -i "$project_root/update.zip" -o "$project_root/update.bin" -m "$app_model" -v "$app_ver"
/bin/cp -f "$project_root/update.bin" "$build_output"
rm -f "$project_root/update.zip"
rm -f "$project_root/update.bin"
fi
# use the auto install tool if present
if [ -f "$auto_install_tool" ]; then
# Ask if the user wants to attempt to auto install the update now. If yes then run the auto install script
read -r -p "Do you want to attempt to auto install the update? [y/N] " response
if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]]; then
# Run the auto update tool
if [[ "$auto_install_tool" == *.py ]]; then
python3 "$auto_install_tool"
else
"$auto_install_tool"
fi
fi
fi
echo
echo -e "${YELLOW}Selected firmware file: $selected_firmware_file ${NC}"
echo -e "${YELLOW}Selected configuration file: $selected_config_file ${NC}"
echo -e "${GREEN}Firmware build complete ${NC}"
echo
exit 0

175
latest/fwdl.sh Normal file
View file

@ -0,0 +1,175 @@
#!/bin/bash
project_root="$PWD"
# Source the utils.sh file
source "$project_root/TOOLS/helpers/utils.sh" "$project_root"
# check the parameters for a model and version
if [ $# != 2 ]; then
echo "usage : $0 <model> <version>"
echo "example: $0 K2Pro 3.0.9"
echo "example: $0 K2Plus \"3.0.0 3.0.5 3.0.9 3.1.0\""
echo "example: $0 K2Max all"
echo "example: $0 K2Pro latest"
echo "example: $0 all all"
echo "example: $0 K2Pro scan"
exit 1
fi
par_models="$1"
par_versions="$2"
stop_after_error=1
downloaded=0
if [ "$par_versions" = "latest" ] || [ "$par_versions" = "LATEST" ] || [ "$par_versions" = "last" ] || [ "$par_versions" = "LAST" ]; then
par_versions=$(curl -s "https://raw.githubusercontent.com/AGG2017/ACK2-Webserver/master/latest_version.txt")
fi
if [ "$par_versions" = "all" ] || [ "$par_versions" = "ALL" ]; then
par_versions="2.3.9 3.0.3 3.0.5 3.0.9 3.1.0 3.1.2"
fi
if [ "$par_versions" = "scan" ] || [ "$par_versions" = "SCAN" ]; then
latest=$(curl -s "https://raw.githubusercontent.com/AGG2017/ACK2-Webserver/master/latest_version.txt")
ver_h=$(echo "$latest" | awk -F. '{print $1}')
ver_m=$(echo "$latest" | awk -F. '{print $2}')
ver_l=$(echo "$latest" | awk -F. '{print $3}')
par_versions=""
for i in {1..100}; do
ver_l=$((ver_l + 1))
if [ $ver_l -ge 10 ]; then
ver_l=0
ver_m=$((ver_m + 1))
if [ $ver_m -ge 10 ]; then
ver_m=0
ver_h=$((ver_h + 1))
fi
fi
version="${ver_h}.${ver_m}.${ver_l}"
if [ -z "$par_versions" ]; then
par_versions="${version}"
else
par_versions="${par_versions} ${version}"
fi
done
stop_after_error=0
fi
if [ "$par_models" = "all" ] || [ "$par_models" = "ALL" ]; then
par_models="K2Pro K2Plus K2Max"
fi
# check the required tools
check_tools "curl wc awk"
for par_model in $par_models; do
# check the model
if [ "$par_model" != "K2Pro" ] && [ "$par_model" != "K2Plus" ] && [ "$par_model" != "K2Max" ]; then
echo -e "${RED}ERROR: Unsupported model '$par_model' ${NC}"
exit 1
fi
for par_version in $par_versions; do
echo -e "${YELLOW}Processing model $par_model version $par_version ...${NC}"
ver_int=${par_version//./}
if [ "$ver_int" -le 309 ]; then
# old url format up to 3.0.9
url_bin="https://cdn.cloud-universe.anycubic.com/ota/${par_model}/AC104_${par_model}_1.1.0_${par_version}_update.bin"
file_bin="FW/AC104_${par_model}_1.1.0_${par_version}_update.bin"
rm -f "$file_bin"
curl "$url_bin" --output "$file_bin"
result=$(grep "<Code>NoSuchKey</Code>" "$file_bin")
file_size=$(wc -c "$file_bin" | awk '{print $1}')
if [ -n "$result" ] || [ "$file_size" -le 1000000 ]; then
rm -f "$file_bin"
# no bin update available, try zip update
url_zip="https://cdn.cloud-universe.anycubic.com/ota/${par_model}/AC104_${par_model}_1.1.0_${par_version}_update.zip"
file_zip="FW/AC104_${par_model}_1.1.0_${par_version}_update.zip"
rm -f "$file_zip"
curl "$url_zip" --output "$file_zip"
result=$(grep "<Code>NoSuchKey</Code>" "$file_zip")
file_size=$(wc -c "$file_zip" | awk '{print $1}')
if [ -n "$result" ] || [ "$file_size" -le 1000000 ]; then
rm -f "$file_zip"
# no bin and no zip update available
echo -e "${RED}ERROR: Cannot find an update for this model and version ${NC}"
if [ $stop_after_error -eq 1 ]; then
exit 3
fi
else
downloaded=$((downloaded + 1))
fi
fi
else
if [ "$ver_int" -le 311 ]; then
# url format 3.1.0
par_model_str="k2PRO"
par_model_id="20021"
if [ "$par_model" = "K2Plus" ]; then
par_model_str="k2PLUS"
par_model_id="20022"
fi
if [ "$par_model" = "K2Max" ]; then
par_model_str="k2MAX"
par_model_id="20023"
fi
url_bin="https://cdn.cloud-universe.anycubic.com/ota/prod/${par_model_id}/AC104_${par_model_str}_V${par_version}.bin"
file_bin="FW/AC104_${par_model}_1.1.0_${par_version}_update.bin"
rm -f "$file_bin"
curl "$url_bin" --output "$file_bin"
result=$(grep "<Code>NoSuchKey</Code>" "$file_bin")
file_size=$(wc -c "$file_bin" | awk '{print $1}')
if [ -n "$result" ] || [ "$file_size" -le 1000000 ]; then
rm -f "$file_bin"
# no bin update available
echo -e "${RED}ERROR: Cannot find an update for this model and version ${NC}"
if [ $stop_after_error -eq 1 ]; then
exit 4
fi
else
downloaded=$((downloaded + 1))
fi
else
# url format 3.1.2+
par_model_str="k2+Pro"
if [ "$par_model" = "K2Plus" ]; then
par_model_str="k2+Plus"
fi
if [ "$par_model" = "K2Max" ]; then
par_model_str="k2+Max"
fi
url_bin="https://cdn.cloud-universe.anycubic.com/ota/${par_model}/ChituUpgrade_${par_model_str}_V${par_version}.bin"
file_bin="FW/AC104_${par_model}_1.1.0_${par_version}_update.bin"
rm -f "$file_bin"
curl "$url_bin" --output "$file_bin"
result=$(grep "<Code>NoSuchKey</Code>" "$file_bin")
file_size=$(wc -c "$file_bin" | awk '{print $1}')
if [ -n "$result" ] || [ "$file_size" -le 1000000 ]; then
rm -f "$file_bin"
# no bin update available
echo -e "${RED}ERROR: Cannot find an update for this model and version ${NC}"
if [ $stop_after_error -eq 1 ]; then
exit 4
fi
else
downloaded=$((downloaded + 1))
fi
fi
fi
done
done
if [ $downloaded -eq 0 ]; then
echo ""
echo -e "${RED}ERROR: Cannot find an update for this model ${NC}"
echo ""
exit 5
fi
echo ""
echo -e "${GREEN}DONE! The requested firmware has been downloaded in the folder FW ${NC}"
echo ""
exit 0

129
latest/options-example.cfg Normal file
View file

@ -0,0 +1,129 @@
#------------------------------------------------------------------------------------------------
# 'options.cfg' is the default configuration file used by 'patch.sh'
# You can provide another configuration file as a parameter to 'patch.sh'
# All available patching options are listed below
# Disable patching an option by using '#' or ';' as a first character in the option line
# Use single or double quotes for the option value. Do not use spaces inside the quotes.
# Use list of values separated by a space if needed because duplicated option are not supported
# In square brackets you may place the name(s) of other option(s) required by a given option.
# This will not auto include the required option(s) but will be used to validate the option integrity.
# IMPORTANT!!! Copy and rename this file to 'options.cfg' and remove the 'example' from the filename.
#-------------------------------------------------------------------------------------------------
# For used with build.sh only!
# Select the input firmware file to be used for this configuration file
#build_input="@/FW/310/update.swu"
# For used with build.sh only!
# Compatible to versions 3.0.5+ only!
# Select the output update file copy of the encrypted result update.bin
#build_output="/data/kobra-unleashed/uploads/updates/20021/310.bin"
# Enable the custom updates by using the provided public key
# When enabled, the new custom update will allow next custom updates to be directly done by USB update
# @ will be replaced by the working folder root
# Use the provided example of public and private keys or generate your own:
# Use 'openssl genrsa -out swupdate_private.pem' to generate a private key
# Use 'openssl rsa -in swupdate_private.pem -out swupdate_public.pem -outform PEM -pubout' to export the public key
# Place both keys (swupdate_private.pem and swupdate_public.pem) in the folder RESOURCES/KEYS
# Available for all firmware versions (recommended)
custom_update="@/RESOURCES/KEYS/swupdate_public.pem"
# Enable root access by providing a custom root password hash (default password 'toor')
# Providing an empty string will allow root access without a password (not recommended)
# If disabled, you will have no root access until you find out what is the AC original root password
# Available for all firmware versions (recommended)
root_access="$1$///xTLYF$krWXTe62/dm.crd6CH4HW0"
# Enable the UART at boot for access the uboot shell and for root login
# UART access is needed for backup/restore and for root login when you don't have ssh
# Use "2.3.9" uboot from version '2.3.9' (recommended)
# From version 3.0.3 the UART is disabled
# Can be used for version 3.0.3 and above
uart="2.3.9"
# Enable opkg (+5MB to the update, +10MB to the rootfs)
# Use it only when you plan to install more packages from the printer
# Available for all firmware versions
opkg="default"
# Enable the SSH server, use 'dropbear' type ssh
# Available for all firmware versions (recommended)
ssh="dropbear"
# Set the authorized keys file for the ssh remote access with keys (instead of a password)
# Provide the file with your public key(s). You must have the private key set in your ssh client
# Available for all firmware versions (recommended)
# authorized_keys="@/RESOURCES/KEYS/authorized_keys" [ssh]
# Enable custom web server
# Use "webfs-v5" with default port 8000 or "webfs-v5:port" for a custom port
# "webfs-v5" uses static libraries with memory footprint less than 400kb (0.4% RAM), no dependencies
# Browse http://printer_ip:8000 to the home page of the custom webserver
# More web pages will be added soon
# Depends on the option "app_nocamera" only for versions 3.0.5+
# No dependency for versions below 3.0.5
# Available for versions 2.3.9 ... 3.1.0 (recommended)
webserver="webfs-v5:8000" [app_nocamera]
# Enable Python 3 (+14MB to the update, +25MB to the rootfs)
# Select the version you need. Python might be required by some other options
# Available for all firmware versions
# python="3.11"
# Change the banner to a custom one. (Recommended when using custom firmware)
# Available for all firmware versions
banner="banner"
# Redirect the MQTT communication
# This option will disable the use of the original AC cloud service
# and the mobile app will stop working
# Can be used when a custom cloud service is needed.
# Example of a custom cloud service with web interface is Kobra Unleashed
# More information: https://github.com/anjomro/kobra-unleashed
# Replace the URL below with the URL of your MQTT server
# You also need to transfer to the printer your keys in
# the /user folder by ssh as explained in the project page
# Always keep a copy of the original keys in case you want to go back to AC cloud
# Available for all firmware versions
modify_mqtt="localhost.mr-a.de"
# localhost.mr-a.de will just redirect the MQTT to the localhost 127.0.0.1 to be used with a local MQTT server
# Delete the Bluetooth support because it's not used by Kobra Unleashed
# and can't be used since we replace mqtt anyways
# Available for versions 3.0.5+
bluetooth="default"
# Patch the app to check the captive portal URLs less often (originally every 2s)
# It is used to detect if the internet connection is established and alive
# This patch will produce less unwanted web traffic if enabled
# Available settings are: 5s, 10s, 20s, 30s and 60s
# Available for versions 3.0.5+
app_net_ready="30s"
# Modify the hardcoded app DNS
# For parameter(s) provide "old_dns|new_dns"
# Available hardcoded DNS in the app:
# "8.8.8.8", "208.67.222.222", "114.114.114.114", "223.5.5.5"
# You can replace one, more or all of them
# Example: app_dns="208.67.222.222|4.4.4.4" "223.5.5.5|8.8.4.4"
app_dns="208.67.222.222|1.1.1.1"
# Patch the app to stop supporting webcams. This allows a custom camera support
# Available for versions 3.0.5+ (recommended if webcam is used)
app_nocamera="default"
# Add a script for custom initializations at startup
# like starting a custom MQTT server, webcam steaming, etc.
# Place startup scripts inside this script.
#startup_script="startup.sh"
# Enable kobra unleashed prod version test
# https://github.com/anjomro/kobra-unleashed/tree/go-server
#kobra_unleashed="kobra_unleashed-v1"
# Enable this option if you want to be executed after the packing script completes
# if you want to install the generated update by ssh
# You have to have ssh and root_access enabled, and setup the auto_install.cfg
#auto_install="@/TOOLS/custom_install.sh"
#auto_install="@/TOOLS/auto_install.py"

130
latest/pack.sh Normal file
View file

@ -0,0 +1,130 @@
#!/bin/bash
project_root="$PWD"
# Source the utils.sh file
source "$project_root/TOOLS/helpers/utils.sh" "$project_root"
# files needed
FILES="sw-description sw-description.sig boot-resource uboot boot0 kernel rootfs dsp0 cpio_item_md5"
# check the required tools
check_tools "grep md5sum openssl wc awk sha256sum mksquashfs"
# remove the last created update
rm -rf update
mkdir update
# pack the squashfs-root folder
cd unpacked || exit 2
echo -e "${YELLOW}Deleting the existing rootfs${NC}"
rm -rf rootfs
mksquashfs squashfs-root rootfs -comp xz -all-root
# check if the updated rootfs can fit in the partitions rootfsA/B
file_size=$(wc -c rootfs | awk '{print $1}')
if [ "$file_size" -ge 134217729 ]; then
echo -e "${RED}ERROR: The size of the file 'unpacked/rootfs' is larger than the max 128MB allowed.\Please disable some of the less important options and try again! ${NC}"
cd ..
exit 3
fi
# check the input files
for i in $FILES; do
if [ "$i" != "cpio_item_md5" ] && [ ! -f "$i" ]; then
echo -e "${RED}ERROR: Cannot find the input file '$i' ${NC}"
cd ..
exit 4
fi
done
# update sw-description
rm -f cpio_item_md5
for i in $FILES; do
if [ "$i" != "cpio_item_md5" ] && [ "$i" != "sw-description" ] && [ "$i" != "sw-description.sig" ]; then
hash_new=$(sha256sum "$i" | awk '{print $1}')
hash_old=$(awk -F= 'BEGIN{v=""} $1~"filename"{v=$2} $1~"sha256"{gsub(/"| |;/,"",v); gsub(/"| |;/,"",$2); print v " " $2}' sw-description | grep "$i" | head -1 | awk '{print $2}')
if [ -n "$hash_old" ]; then
sed -i -e "s/$hash_old/$hash_new/g" sw-description
else
echo -e "${RED}ERROR: Cannot find the hash for: '$i' ${NC}"
cd ..
exit 5
fi
fi
done
# create cpio_item_md5
rm -f cpio_item_md5
for i in $FILES; do
if [ "$i" != "cpio_item_md5" ]; then
hash=$(md5sum "$i")
echo "$hash" >>cpio_item_md5
fi
done
# sign the file sw-description
rm -f sw-description.sig
openssl dgst -sha256 -sign ../RESOURCES/KEYS/swupdate_private.pem sw-description >sw-description.sig
# pack the input files as update.swu
for i in $FILES; do echo "$i"; done | cpio -ov -H crc >../update/update.swu
cd ..
echo ""
echo -e "${GREEN}Packing done: Use the file update/update.swu to do USB update${NC}"
echo ""
# select a config file
selected_config_file="options.cfg"
if [ $# -eq 1 ]; then
cfg_file="$project_root/$1"
if [ -f "$cfg_file" ]; then
# it is a configuration file with ext
selected_config_file="$cfg_file"
elif [ -f "${cfg_file}.cfg" ]; then
echo "${cfg_file}.cfg"
# it is a configuration file without ext
selected_config_file="${cfg_file}.cfg"
fi
fi
# check if the auto update is enabled and get the selected tool
auto_install_tool=""
if [ -f "$selected_config_file" ]; then
# parse the enabled options that have a set value
options=$(awk -F '=' '{if (! ($0 ~ /^;/) && ! ($0 ~ /^#/) && ! ($0 ~ /^$/) && ! ($2 == "")) print $1}' "$selected_config_file")
# for each enabled option
for option in $options; do
parameters=$(awk -F '=' "{if (! (substr(\$0,1,1) == \"#\") && ! (substr(\$0,1,1) == \";\") && ! (\$1 == \"\") && ! (\$2 == \"\") && (\$1 ~ /$option/ ) ) print \$2}" "$selected_config_file" | head -n 1)
# replace the project root requests
parameter="${parameters/@/"$project_root"}"
# remove the leading and ending double quotes
parameter=$(echo "$parameter" | sed -e 's/^"//' -e 's/"$//')
# remove the leading and ending single quotes
parameter=$(echo "$parameter" | sed -e 's/^'\''//' -e 's/'\''$//')
if [ "$option" = "auto_install" ]; then
auto_install_tool="$parameter"
fi
done
fi
# use the auto install tool if present
if [ -f "$auto_install_tool" ]; then
# Ask if the user wants to attempt to auto install the update now. If yes then run the auto install script
read -r -p "Do you want to attempt to auto install the update? [y/N] " response
if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]]; then
# Run the auto update tool
if [[ "$auto_install_tool" == *.py ]]; then
python3 "$auto_install_tool"
else
"$auto_install_tool"
fi
fi
fi
exit 0

99
latest/patch.sh Normal file
View file

@ -0,0 +1,99 @@
#!/bin/bash
project_root="$PWD"
# Source the utils.sh file
source "$project_root/TOOLS/helpers/utils.sh" "$project_root"
# the default options file
optionsfile="options.cfg"
# the result of installed options log
installed_options="installed_options.log"
# check the parameters for a custom options file
if [ $# == 1 ]; then
optionsfile="$1"
if [ ! -f "$optionsfile" ]; then
optionsfile="${optionsfile}.cfg"
fi
fi
# check the options file
if [ ! -f "$optionsfile" ]; then
echo -e "${RED}ERROR: Cannot find the '$optionsfile' file ${NC}"
exit 1
fi
check_tools "awk head sed"
# remove the old result file for the installed options
rm -f "$installed_options"
# parse the enabled options that have a set value
options=$(awk -F '=' '{if (! ($0 ~ /^;/) && ! ($0 ~ /^#/) && ! ($0 ~ /^$/) && ! ($2 == "")) print $1}' "$optionsfile")
# for each enabled option
for option in $options; do
# skip the options build_input & build_output that are used only in build.sh
# skip the option auto_install that is used only in pack.sh
if [ "$option" = "build_input" ] || [ "$option" = "build_output" ] || [ "$option" = "auto_install" ]; then
continue
fi
echo -e "${PURPLE}Processing option '$option' ...${NC}"
# parse the parameters (only from the first found option)
# duplicated options are not supported, if needed use list of parameters for the same option:
# startup_script="script1.sh" "script2.sh" "script3.sh"
parameters=$(awk -F '=' "{if (! (substr(\$0,1,1) == \"#\") && ! (substr(\$0,1,1) == \";\") && ! (\$1 == \"\") && ! (\$2 == \"\") && (\$1 ~ /$option/ ) ) print \$2}" "$optionsfile" | head -n 1)
# replace the project root requests
parameters="${parameters/@/"$project_root"}"
# execute the script
opt_script="${OPTIONS_DIR}/${option}/${option}.sh"
if [ ! -f "$opt_script" ]; then
echo -e "${RED}ERROR: Cannot find the file '$opt_script' ${NC}"
exit 3
fi
# for each parameter in the list
for parameter in $parameters; do
# remove the leading and ending double quotes
param=$(echo "$parameter" | sed -e 's/^"//' -e 's/"$//')
# remove the leading and ending single quotes
par=$(echo "$param" | sed -e 's/^'\''//' -e 's/'\''$//')
# remove the leading and ending square brackets
req_option=$(echo "$par" | sed -e 's/^\[//' -e 's/\]$//')
if [ "$par" == "$req_option" ]; then
# current parameter requires processing
"$opt_script" "$project_root" "$par"
if [ $? -ne 0 ]; then
# errors found, exit
echo "Errors found! The patching has been canceled."
exit 4
fi
# set this option as already installed
echo "$option=\"$par\"" >>"$installed_options"
else
# this is an option requirement that needs validation
found=""
for opt in $options; do
if [ "$opt" == "$req_option" ]; then
found="$opt"
break
fi
done
if [ -z "$found" ]; then
# required option is missing or not enabled
echo -e "${RED}ERROR: Option '$option' requires option '$req_option' which is missing or not enabled. ${NC}"
exit 5
fi
echo -e "${GREEN}Option '$option' requires option '$req_option'. This requirement was successfully validated. ${NC}"
fi
done
done
echo ""
echo -e "${GREEN}DONE! The selected options are successfully processed.${NC}\nYou may do manually more changes in the 'unpacked' folder if needed."
echo ""
exit 0

3
latest/requirements.txt Normal file
View file

@ -0,0 +1,3 @@
pycryptodome==3.21.0
paramiko==3.5.0
scp==0.15.0

101
latest/unpack.sh Normal file
View file

@ -0,0 +1,101 @@
#!/bin/bash
project_root="$PWD"
# Source the utils.sh file
source "$project_root/TOOLS/helpers/utils.sh" "$project_root"
# check the number of arguments
if (($# != 1)); then
echo "Usage: ./unpack.sh update_file"
echo "Example: ./unpack.sh FW/AC104_K2Pro_1.1.0_3.0.5_update.bin"
exit 1
fi
UPDATE_FILE="$1"
UPDATE_FILENAME=$(basename -- "$UPDATE_FILE")
UPDATE_FILE_EXT="${UPDATE_FILENAME##*.}"
# check the input file
if [ ! -f "$UPDATE_FILE" ]; then
echo -e "${RED}ERROR: Cannot find the input file ${UPDATE_FILE}${NC}"
exit 1
fi
# check the input file ext
if [ "$UPDATE_FILE_EXT" != "bin" ] && [ "$UPDATE_FILE_EXT" != "swu" ] && [ "$UPDATE_FILE_EXT" != "zip" ]; then
echo -e "${RED}ERROR: Unknown file extension '${UPDATE_FILE_EXT}'${NC}"
exit 2
fi
# check the required tools
check_tools "cpio unsquashfs unzip ack2_swu_decrypt.py python3"
# set the custom decrypt tool
DECRYPT_TOOL=$(which "ack2_swu_decrypt.py")
if [ -z "$DECRYPT_TOOL" ]; then
# if not installed use the local copy
DECRYPT_TOOL="TOOLS/ack2_swu_decrypt.py"
fi
# remove old temp files if present
rm -rf unpacked
mkdir unpacked
# preprocessing the update file
if [ "$UPDATE_FILE_EXT" == "bin" ]; then
# decrypt the update if it is encrypted
$DECRYPT_TOOL -i "$UPDATE_FILE" -o ./unpacked/update.zip
if [ ! -f "./unpacked/update.zip" ]; then
echo -e "${RED}ERROR: Cannot find the input file './unpacked/update.zip' ${NC}"
exit 4
fi
cd unpacked || exit 5
unzip update.zip
rm -r update.zip
cd ..
else
if [ "$UPDATE_FILE_EXT" == "zip" ]; then
# zip file
cp "$UPDATE_FILE" ./unpacked/update.zip
cd unpacked || exit 5
unzip update.zip
rm -r update.zip
cd ..
else
# swu file, prepare a copy of the file
mkdir ./unpacked/update
cp "$UPDATE_FILE" ./unpacked/update/update.swu
fi
fi
# check the input file
if [ ! -f "./unpacked/update/update.swu" ]; then
echo -e "${RED}ERROR: Cannot find the input file './unpacked/update/update.swu' ${NC}"
exit 6
fi
# extract the update
cd unpacked || exit 7
cpio -idv <./update/update.swu
# verify that all needed parts exist
FILES="sw-description sw-description.sig boot-resource uboot boot0 kernel rootfs dsp0 cpio_item_md5"
for i in $FILES; do
if [ ! -f "$i" ]; then
echo -e "${RED}ERROR: Cannot find the expected update component '$i' ${NC}"
cd ..
exit 8
fi
done
# unpack the rootfs
unsquashfs rootfs
cd ..
echo ""
echo -e "${GREEN}Unpacking DONE! Check the 'unpacked' folder for the result.${NC}"
echo ""
exit 0