Skip to content

Instantly share code, notes, and snippets.

@nobodyguy
Last active October 30, 2025 19:36
Show Gist options
  • Select an option

  • Save nobodyguy/d97808cc1570a7676962c9ae609423f9 to your computer and use it in GitHub Desktop.

Select an option

Save nobodyguy/d97808cc1570a7676962c9ae609423f9 to your computer and use it in GitHub Desktop.
Small python script to update Bambulab A1 Mini or P1S in LAN mode with older firmware (without offline SD card update) to the first version that enables offline updates. Feel free to modify it for other printers and firmware versions.
#!/usr/bin/env python3
"""
Bambulab Printer OTA Firmware Update Script
This script connects to your Bambulab printer (A1 Mini or P1S) via MQTT
and initiates an OTA firmware update.
Sources:
- https://github.com/lunDreame/user-bambulab-firmware/issues/15
- https://m.cafe.naver.com/ca-fe/web/cafes/30884640/articles/3353
"""
import paho.mqtt.client as mqtt
import json
import ssl
import time
import sys
# ============================================================================
# PRINTER CONFIGURATION
# ============================================================================
# Select your printer model: "A1_MINI" or "P1S"
PRINTER_MODEL = "A1_MINI" # Change to "P1S" for P1 Series printers
# AMS Configuration
HAS_AMS = False # Set to True if your printer has an AMS unit attached
# Connection Configuration
PRINTER_IP = "YOUR_PRINTER_IP" # Replace with your printer's IP address
MQTT_PORT = 8883
MQTT_USERNAME = "bblp"
MQTT_PASSWORD = "YOUR_8_DIGIT_ACCESS_CODE" # Replace with your 8-digit access code
PRINTER_SERIAL = "YOUR_PRINTER_SERIAL" # Replace with your printer's serial number
# ============================================================================
# FIRMWARE CONFIGURATIONS
# ============================================================================
FIRMWARE_CONFIGS = {
"A1_MINI": {
"name": "Bambu Lab A1 Mini",
"version": "01.04.00.00",
"url": "https://public-cdn.bblmw.com/upgrade/device/N1/01.04.00.00/product/1496eccbb7/ota-n1_v01.04.00.00-20241210144819.json.sig",
"description": "",
"ams": [] # A1 Mini typically doesn't have AMS firmware updates in the same way
},
"P1S": {
"name": "Bambu Lab P1S",
"version": "01.07.00.00",
"url": "https://public-cdn.bblmw.com/upgrade/device/C11/01.07.00.00/product/3b63468a6f/ota-p003_v01.07.00.00-20241210145014.json.sig",
"description": "",
"ams": [
{
"dev_model_name": "BL-A001",
"address": 0,
"device_id": "",
"firmware": [
{
"version": "00.00.06.49",
"force_update": False,
"url": "https://public-cdn.bblmw.com/upgrade/device/BL-A001/00.00.06.49/product/ef67633af9/ams-ota_v00.00.06.49-20241021110111.json.sig",
"description": "",
"status": "testing"
}
],
"firmware_current": None
}
]
}
}
# Get current printer configuration
if PRINTER_MODEL not in FIRMWARE_CONFIGS:
print(f"❌ Error: Unknown printer model '{PRINTER_MODEL}'")
print(f" Supported models: {', '.join(FIRMWARE_CONFIGS.keys())}")
sys.exit(1)
PRINTER_CONFIG = FIRMWARE_CONFIGS[PRINTER_MODEL]
FIRMWARE_VERSION = PRINTER_CONFIG["version"]
FIRMWARE_URL = PRINTER_CONFIG["url"]
FIRMWARE_DESCRIPTION = PRINTER_CONFIG["description"]
# Only include AMS firmware if HAS_AMS is True
AMS_FIRMWARE = PRINTER_CONFIG["ams"] if HAS_AMS else []
# MQTT Topics
COMMAND_TOPIC = f"device/{PRINTER_SERIAL}/request"
STATUS_TOPIC = f"device/{PRINTER_SERIAL}/report"
# Connection status
connected = False
def on_connect(client, userdata, flags, rc):
"""Callback when connected to MQTT broker"""
global connected
if rc == 0:
print("βœ“ Successfully connected to printer")
connected = True
# Subscribe to status updates
client.subscribe(STATUS_TOPIC)
print(f"βœ“ Subscribed to status topic: {STATUS_TOPIC}")
else:
print(f"βœ— Connection failed with code {rc}")
error_messages = {
1: "Incorrect protocol version",
2: "Invalid client identifier",
3: "Server unavailable",
4: "Bad username or password",
5: "Not authorized"
}
print(f" Error: {error_messages.get(rc, 'Unknown error')}")
connected = False
def on_disconnect(client, userdata, rc):
"""Callback when disconnected from MQTT broker"""
global connected
connected = False
if rc != 0:
print(f"βœ— Unexpected disconnection (code: {rc})")
def on_message(client, userdata, msg):
"""Callback when a message is received"""
try:
payload = json.loads(msg.payload.decode())
print(f"\nπŸ“© Received message on {msg.topic}:")
print(json.dumps(payload, indent=2))
# Check for upgrade status
if "upgrade" in payload:
upgrade_status = payload["upgrade"]
if "status" in upgrade_status:
print(f"\nπŸ”„ Update Status: {upgrade_status['status']}")
if "progress" in upgrade_status:
print(f"πŸ“Š Progress: {upgrade_status['progress']}%")
except json.JSONDecodeError:
print(f"πŸ“© Received non-JSON message: {msg.payload.decode()}")
except Exception as e:
print(f"⚠ Error processing message: {e}")
def on_publish(client, userdata, mid):
"""Callback when a message is published"""
print(f"βœ“ Message published (ID: {mid})")
def send_ota_update(client):
"""Send OTA firmware update command to printer"""
update_command = {
"user_id": "0",
"upgrade": {
"sequence_id": "0",
"command": "upgrade_history",
"src_id": 2,
"firmware_optional": {
"firmware": {
"version": FIRMWARE_VERSION,
"url": FIRMWARE_URL,
"force_update": False,
"description": FIRMWARE_DESCRIPTION,
"status": "release"
},
"ams": AMS_FIRMWARE
}
}
}
print("\nπŸ“€ Sending OTA update command...")
print(f" Printer Model: {PRINTER_CONFIG['name']}")
print(f" Firmware Version: {FIRMWARE_VERSION}")
print(f" Update URL: {FIRMWARE_URL}")
print(f" Command: upgrade_history")
if HAS_AMS and AMS_FIRMWARE:
print(f" AMS Updates: {len(AMS_FIRMWARE)} device(s)")
elif HAS_AMS and not AMS_FIRMWARE:
print(f" AMS Updates: None available for this printer model")
else:
print(f" AMS Updates: Skipped (HAS_AMS=False)")
result = client.publish(
COMMAND_TOPIC,
json.dumps(update_command),
qos=1
)
if result.rc == mqtt.MQTT_ERR_SUCCESS:
print("βœ“ Update command sent successfully")
return True
else:
print(f"βœ— Failed to send update command (error code: {result.rc})")
return False
def main():
"""Main function to run the OTA update"""
global connected
# Validate configuration
if PRINTER_IP == "YOUR_PRINTER_IP":
print("⚠ Error: Please update PRINTER_IP in the script with your printer's IP address")
sys.exit(1)
if MQTT_PASSWORD == "YOUR_8_DIGIT_ACCESS_CODE":
print("⚠ Error: Please update MQTT_PASSWORD with your 8-digit access code")
sys.exit(1)
if PRINTER_SERIAL == "YOUR_PRINTER_SERIAL":
print("⚠ Error: Please update PRINTER_SERIAL with your printer's serial number")
sys.exit(1)
print("=" * 60)
print("Bambulab Printer OTA Firmware Update")
print("=" * 60)
print(f"Printer Model: {PRINTER_CONFIG['name']}")
print(f"Printer IP: {PRINTER_IP}")
print(f"Target Version: {FIRMWARE_VERSION}")
print(f"AMS Present: {'Yes' if HAS_AMS else 'No'}")
print("=" * 60)
# Create MQTT client with MQTT v3.1.1
client = mqtt.Client(
client_id="bambulab_updater",
protocol=mqtt.MQTTv311 # MQTT Version 3.1.1
)
# Set username and password
client.username_pw_set(MQTT_USERNAME, MQTT_PASSWORD)
# Configure TLS/SSL (SSL/TLS: On, SSL Secure: Off)
client.tls_set(cert_reqs=ssl.CERT_NONE)
client.tls_insecure_set(True)
# Set callbacks
client.on_connect = on_connect
client.on_disconnect = on_disconnect
client.on_message = on_message
client.on_publish = on_publish
# Connect to printer
print(f"\nπŸ”Œ Connecting to printer at {PRINTER_IP}:{MQTT_PORT}...")
try:
client.connect(PRINTER_IP, MQTT_PORT, keepalive=60)
# Start network loop in background
client.loop_start()
# Wait for connection
timeout = 10
elapsed = 0
while not connected and elapsed < timeout:
time.sleep(0.5)
elapsed += 0.5
if not connected:
print("βœ— Connection timeout. Please check:")
print(" - Printer IP address is correct")
print(" - Access code is correct")
print(" - Printer is powered on and connected to network")
client.loop_stop()
sys.exit(1)
# Send OTA update command
time.sleep(1) # Brief pause after connection
if send_ota_update(client):
print("\n⏳ Monitoring update progress...")
print(" (Press Ctrl+C to stop monitoring)\n")
# Keep running to receive status updates
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print("\n\n⏹ Stopped monitoring. Update continues on printer.")
except Exception as e:
print(f"\nβœ— Error: {e}")
finally:
client.loop_stop()
client.disconnect()
print("\nπŸ‘‹ Disconnected from printer")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment