From 47012895687da1896293bcac8d297c79b5bb37ee Mon Sep 17 00:00:00 2001 From: Ember Light Date: Mon, 16 Jun 2025 13:18:08 +0200 Subject: [PATCH 01/22] Add AMS options --- firmware/Makefile | 6 +- firmware/common-manual-ams.yaml | 104 +++++ firmware/conf.d/bambu_ams.yaml | 90 +++++ firmware/conf.d/pn532_rfid-manual-ams.yaml | 399 +++++++++++++++++++ firmware/conf.d/web_server.yaml | 33 +- firmware/esp32-s3-super-mini-manual-ams.yaml | 28 ++ firmware/openspool-ams.yaml | 4 +- firmware/openspool-manual-ams.yaml | 4 + firmware/openspool-mini.yaml | 4 +- 9 files changed, 652 insertions(+), 20 deletions(-) create mode 100644 firmware/common-manual-ams.yaml create mode 100644 firmware/conf.d/bambu_ams.yaml create mode 100644 firmware/conf.d/pn532_rfid-manual-ams.yaml create mode 100644 firmware/esp32-s3-super-mini-manual-ams.yaml create mode 100644 firmware/openspool-manual-ams.yaml diff --git a/firmware/Makefile b/firmware/Makefile index b45fa23..8620424 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -17,6 +17,7 @@ clean: esphome clean lolin_s2_mini.yaml esphome clean lolin_s3_mini.yaml esphome clean esp32-s3-devkitc-1.yaml + esphome clean esp32_s3_super_mini.yaml rm -rf .esphome/idedata/* rm -rf ~/.platformio/penv # build-esp32s2-mini: @@ -71,4 +72,7 @@ lolin_s3_mini: run lolin_s3_mini.yaml --device $(USB_ADDRESS) devkit: esphome \ - run esp32-s3-devkitc-1.yaml --device $(USB_ADDRESS) \ No newline at end of file + run esp32-s3-devkitc-1.yaml --device $(USB_ADDRESS) +s3_super_mini_manual_ams: + esphome \ + run esp32-s3-super-mini-manual-ams.yaml --device $(USB_ADDRESS) \ No newline at end of file diff --git a/firmware/common-manual-ams.yaml b/firmware/common-manual-ams.yaml new file mode 100644 index 0000000..31c6178 --- /dev/null +++ b/firmware/common-manual-ams.yaml @@ -0,0 +1,104 @@ +--- +substitutions: + name: openspool +esphome: + name: ${name} + name_add_mac_suffix: true + project: + name: spuder.openspool + version: ${version} + min_version: 2024.11.0 + platformio_options: + build_unflags: -std=gnu++11 + build_flags: + - -std=gnu++14 + - -DMBEDTLS_CONFIG_FILE=\"mbedtls/esp_config.h\" + on_boot: + then: + #TODO: breahting blue studders a little bit + - light.turn_on: + id: neopixel_light + effect: Breathing Blue + brightness: 100% + - wait_until: + condition: + wifi.connected: + - delay: 100ms + - light.turn_on: + id: neopixel_light + effect: none + - delay: 100ms + - light.turn_on: + id: neopixel_light + effect: Rainbow + - delay: 1s + - light.turn_off: + id: neopixel_light + - delay: 500ms + - script.execute: update_leds + - if: + condition: + lambda: |- + return !id(bambu_lan_access_code).state.empty() && + !id(bambu_ip_address).state.empty() && + !id(bambu_serial_number).state.empty(); + then: + - logger.log: + level: info + format: "Connecting to Bambu printer" + - mqtt.enable: + id: bambu_mqtt + else: + - logger.log: + level: info + format: "Missing Bambu Credentials, skipping mqtt connect" + - mqtt.disable: + id: bambu_mqtt + # - script.execute: set_all_leds_white + # - lambda: |- + # //TODO: this appears to have broken and no longer blinks blue + # if (!wifi::global_wifi_component->is_connected() && + # wifi::global_wifi_component->wifi_soft_ap_ip().str() == "192.168.4.1") { + # id(set_led_breathing_blue).execute(); + # } + # - lambda: |- + # id(my_ota).set_auth_password("New password"); + on_shutdown: + then: + - script.execute: set_leds_off +esp32: + framework: + type: esp-idf + version: 5.3.1 + platform_version: 6.9.0 # https://github.com/platformio/platform-espressif32/releases/ + sdkconfig_options: + CONFIG_MBEDTLS_HKDF_C: y # Needed for bambu KDF + CONFIG_MBEDTLS_MD_C: y # Needed for bambu KDF + # version: recommended + # sdkconfig_options: + # MBEDTLS_CERTIFICATE_BUNDLE: y + # MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL: y + +#TODO: uncomment when ready for bambu support +# external_components: +# - source: +# type: local +# path: components +# components: [pn532, nfc] + +packages: + version: !include conf.d/version.yaml + uptime: !include conf.d/uptime.yaml + wifi: !include conf.d/wifi.yaml + web_server: !include conf.d/web_server.yaml + debug: !include conf.d/debug.yaml + # time: !include conf.d/time.yaml + mqtt_bambu_lan: !include conf.d/mqtt_bambu_lan.yaml + filament: !include conf.d/filament.yaml + bambu_printer: !include conf.d/bambu_printer.yaml + automation: !include conf.d/automation.yaml + led-external: !include conf.d/led-external.yaml + ota: !include conf.d/ota.yaml + #update: !include conf.d/update.yaml + api: !include conf.d/api.yaml + logger: !include conf.d/logger.yaml diff --git a/firmware/conf.d/bambu_ams.yaml b/firmware/conf.d/bambu_ams.yaml new file mode 100644 index 0000000..d5581d2 --- /dev/null +++ b/firmware/conf.d/bambu_ams.yaml @@ -0,0 +1,90 @@ +select: + - platform: template + name: "Number of AMS units" + id: ams_units + optimistic: false + restore_value: true + icon: mdi:printer-3d-nozzle + options: + - "1" + - "2" + - "3" + - "4" + initial_option: "1" + set_action: + then: + - lambda: |- + if (id(bambu_model).state == "A1Mini" || id(bambu_model).state == "A1") { + id(ams_units).publish_state("1"); + } else { + id(ams_units).publish_state(x); + } + on_value: + then: + - lambda: |- + ESP_LOGD("AMS", "AMS units set: %s", x.c_str()); + web_server: + sorting_group_id: sorting_group_printer_settings + sorting_weight: 100 + + - platform: template + name: "AMS Number" + id: ams_number + optimistic: false + restore_value: true + icon: mdi:printer-3d-nozzle + options: + - "0" + - "1" + - "2" + - "3" + - "4" + initial_option: "1" + set_action: + then: + - lambda: |- + if (id(bambu_model).state == "A1Mini" || id(bambu_model).state == "A1") { + id(ams_number).publish_state("1"); + } else { + if (x == "0") { + id(slot_number).publish_state("1"); + } + id(ams_number).publish_state(x); + } + on_value: + then: + - lambda: |- + ESP_LOGD("AMS", "AMS selected: %s", x.c_str()); + web_server: + sorting_group_id: sorting_group_ams + + - platform: template + name: "Slot Number" + id: slot_number + optimistic: false + restore_value: true + icon: mdi:printer-3d-nozzle + options: + - "1" + - "2" + - "3" + - "4" + initial_option: "1" + set_action: + then: + - lambda: |- + if (id(ams_number).state == "0") { + // Only force to 1 if manually selecting through UI, not from encoder + if (x != id(slot_number).state) { + id(slot_number).publish_state("1"); + ESP_LOGD("AMS", "Forcing slot to 1 because AMS is 0"); + } + } else { + id(slot_number).publish_state(x); + } + on_value: + then: + - lambda: |- + ESP_LOGD("AMS", "Slot selected: %s", x.c_str()); + web_server: + sorting_group_id: sorting_group_ams diff --git a/firmware/conf.d/pn532_rfid-manual-ams.yaml b/firmware/conf.d/pn532_rfid-manual-ams.yaml new file mode 100644 index 0000000..d15aadb --- /dev/null +++ b/firmware/conf.d/pn532_rfid-manual-ams.yaml @@ -0,0 +1,399 @@ +--- +substitutions: + spi_data_rate: 200kHz + update_interval: "750ms" + +globals: + - id: selected_slot + type: int + restore_value: yes + initial_value: "0" + +script: + - id: update_leds + then: + - lambda: |- + auto ams_index = id(ams_number).active_index(); + auto slot_index = id(slot_number).active_index(); + for (int i = 0; i < ams_index.value() * slot_index.value(); i++) { + if (i == id(selected_slot)) { + id(set_led_blue).execute(i); + } else { + id(set_led_off).execute(i); + } + } + - id: publish_filament_data_to_mqtt + parameters: + payload_data: string + then: + - if: + condition: + and: + - mqtt.connected + - lambda: 'return payload_data != "";' + - lambda: 'return payload_data != "{}";' + - binary_sensor.is_on: rfid_reader_spi_0_tag_is_openspool + - binary_sensor.is_on: rfid_reader_spi_0_tag_parsed + then: + - mqtt.publish: + topic: !lambda 'return "device/" + id(bambu_serial_number).state + "/request";' + payload: !lambda |- + // 255 = external ams, first ams is 0 + // 254 = external tray, first filament slot is tray 0 + uint16_t ams_id = 255; // Default to external AMS + uint16_t tray_id = 254; // Default to external tray + + if (id(ams_number).active_index().value() > 0) { + ams_id = id(ams_number).active_index().value(); + tray_id = id(slot_number).active_index().value(); + } + + return bambulabs::generate_mqtt_payload(payload_data, ams_id, tray_id); + +sensor: + - platform: rotary_encoder + id: encoder_sensor + name: "Encoder Position" + resolution: 2 + pin_a: + number: ${encoder_pin_a} + mode: + input: true + pullup: true + pin_b: + number: ${encoder_pin_b} + mode: + input: true + pullup: true + on_clockwise: + then: + - lambda: |- + if(id(ams_number).state == "0") { + id(ams_number).publish_state("1"); + } else if (id(slot_number).state == "4") { + if (id(ams_number).state == id(ams_units).state) { + id(ams_number).publish_state("0"); + } else { + auto cur_index = id(ams_number).active_index().value(); + auto option = id(ams_number).at(cur_index + 1).value(); + id(ams_number).publish_state(option.c_str()); + } + id(slot_number).publish_state("1"); + } else { + // Increment the slot directly using the current state value + int current_slot = std::stoi(id(slot_number).state); + int next_slot = current_slot + 1; + // Make sure it stays in valid range + if (next_slot > 4) next_slot = 4; + id(slot_number).publish_state(std::to_string(next_slot).c_str()); + + // Debugging output + ESP_LOGD("Encoder", "Incrementing slot from %d to %d", current_slot, next_slot); + } + // Force state and UI update + if (id(slot_number).state == "3" || id(slot_number).state == "4") { + // This bypasses normal mechanisms to force the UI update + id(slot_number).state = id(slot_number).state; + id(slot_number).publish_state(id(slot_number).state); + } + - script.execute: update_leds + on_anticlockwise: + then: + - lambda: |- + if (id(ams_number).state == "0") { + // If at AMS 0, go to max AMS and max slot + id(ams_number).publish_state(id(ams_units).state.c_str()); + id(slot_number).publish_state("4"); + } else if (id(slot_number).state == "1") { + // If at slot 1, go to previous AMS and max slot + auto cur_ams = id(ams_number).state; + if (cur_ams == "1") { + // If at AMS 1, go to AMS 0 + id(ams_number).publish_state("0"); + id(slot_number).publish_state("1"); + } else { + // Otherwise, go to previous AMS and max slot + int prev_ams = std::stoi(cur_ams) - 1; + id(ams_number).publish_state(std::to_string(prev_ams).c_str()); + id(slot_number).publish_state("4"); + } + } else { + // Just decrement the slot - but do it directly using the current state value + int current_slot = std::stoi(id(slot_number).state); + int prev_slot = current_slot - 1; + // Make sure it stays in valid range + if (prev_slot < 1) prev_slot = 1; + id(slot_number).publish_state(std::to_string(prev_slot).c_str()); + + // Debugging output + ESP_LOGD("Encoder", "Decrementing slot from %d to %d", current_slot, prev_slot); + } + - script.execute: update_leds + +binary_sensor: + - platform: template + name: "NFC Tag Present" + id: nfc_tag_present0 + state_topic: + icon: mdi:circle-double + web_server: + sorting_group_id: sorting_group_rfid + +spi: + # On esp32-s3 SPI2 a can support 6 devices, whereas only 3 on bus SPI3 + - id: SPI2 + clk_pin: ${spi2_clk_pin} # SCK + miso_pin: ${spi2_miso_pin} # MO/SDA/TX (MISO) + mosi_pin: ${spi2_mosi_pin} # M (MOSI) + interface: ${spi2_type} + +pn532_spi: + - id: rfid_reader_spi_0 + cs_pin: ${rfid0_ss_pin} # NSS/SCL/RX + spi_id: ${rfid0_spi_interface} + data_rate: ${spi_data_rate} + update_interval: ${update_interval} + on_tag_removed: + then: + - binary_sensor.template.publish: + id: nfc_tag_present0 + state: OFF + - lambda: |- + id(filament_raw_data0).publish_state(""); + id(set_led_blue).execute(id(selected_slot)); + on_tag: + then: + - binary_sensor.template.publish: + id: nfc_tag_present0 + state: ON + - lambda: |- + bool is_valid_openspool = false; + std::string payload; + + if (tag.has_ndef_message()) { + auto records = tag.get_ndef_message()->get_records(); + bool found_json = false; + + // Look for application/json record + for (const auto& record : records) { + if (record->get_type() == "application/json") { + if (found_json) { + // More than one JSON record found + ESP_LOGW("NFC", "Multiple JSON records found, using first one"); + break; + } + + payload = record->get_payload(); + ESP_LOGD("NFC", "Payload: %s", payload.c_str()); + id(filament_raw_data0).publish_state(payload); + + // Parse JSON and check for OpenSpool protocol + auto parse_result = json::parse_json(payload, [&](JsonObject root) { + is_valid_openspool = root["protocol"] == "openspool"; + return true; + }); + + if (!parse_result) { + ESP_LOGE("NFC", "Failed to parse JSON payload"); + } + + found_json = true; + } + } + + if (!found_json) { + ESP_LOGW("NFC", "No application/json record found"); + } + } else { + ESP_LOGI("NFC", "Tag found without NDEF message"); + } + + // Update states + id(rfid_reader_spi_0_tag_parsed).publish_state(true); + id(rfid_reader_spi_0_tag_is_openspool).publish_state(is_valid_openspool); + + // Set LED color + if (is_valid_openspool) { + id(set_led_green).execute(id(selected_slot)); + //TODO: Generate and send MQTT message here + if (!payload.empty()) { + id(publish_filament_data_to_mqtt).execute(payload); + } + } else { + id(set_led_red).execute(id(selected_slot)); + } + +text_sensor: + - platform: template + name: "NFC Raw Data" + id: filament_raw_data0 + state_topic: + internal: false # Always show raw data 0 + icon: mdi:nfc-variant + web_server: + sorting_group_id: sorting_group_rfid + filters: + - lambda: |- + auto pretty_json = [](const std::string &x) -> std::string { + if (x.empty()) { + ESP_LOGD("NFC", "Input string is empty"); + return x; + } + ESP_LOGD("NFC", "Input string: %s", x.c_str()); + + StaticJsonDocument<1024> doc; // Use StaticJsonDocument for memory efficiency + DeserializationError error = deserializeJson(doc, x); + if (error) { + ESP_LOGE("NFC", "JSON parsing failed: %s", error.c_str()); + return ""; + } + + if (!doc.is()) { + ESP_LOGE("NFC", "Invalid JSON: Not an object"); + return ""; + } + + const char* required_fields[] = {"protocol", "color_hex", "type", "min_temp", "max_temp", "brand"}; + for (const char* field : required_fields) { + if (!doc.containsKey(field)) { + ESP_LOGE("NFC", "Invalid JSON: Missing required field '%s'", field); + return ""; + } + } + + std::string output; + serializeJsonPretty(doc, output); + + if (output.length() > 1024) { + ESP_LOGE("NFC", "Prettified JSON exceeds 1024 bytes"); + return ""; + } + + return output; + }; + return pretty_json(x); + + - platform: template + name: "NFC Preview" + id: nfc_preview + state_topic: + icon: mdi:nfc-search-variant + web_server: + sorting_group_id: sorting_group_rfid + +# Create virtual button that can be pressed in the gui +button: + - platform: template + name: "Write NFC" + id: write_nfc + state_topic: + icon: mdi:nfc-tap + web_server: + sorting_group_id: sorting_group_filament_settings + sorting_weight: 210 + on_press: + then: + - if: + condition: + and: + - lambda: |- + if (id(filament_brand).state == "") { + ESP_LOGE("main", "Filament Brand cannot be empty"); + return false; + } else { + return true; + } + - lambda: |- + if (id(filament_brand_code).state == "") { + ESP_LOGE("main", "Filament Brand Code cannot be empty"); + return false; + } else { + return true; + } + - lambda: |- + if (id(filament_color_hex).state == "") { + ESP_LOGE("main", "Filament Color Hex cannot be empty"); + return false; + } else { + return true; + } + - lambda: |- + if (id(filament_max_temp).state > 300) { + ESP_LOGE("main", "Filament Max Temp must be less than 300"); + return false; + } else { + return true; + } + - lambda: |- + if (id(filament_min_temp).state < 150) { + ESP_LOGE("main", "Filament Min Temp must be greater than 150"); + return false; + } else { + return true; + } + - lambda: |- + if (id(filament_min_temp).state > id(filament_max_temp).state) { + ESP_LOGE("main", "Filament Min Temp must be less than Filament Max Temp"); + return false; + } else { + return true; + } + - lambda: |- + if (id(filament_type).state == "") { + ESP_LOGE("main", "Filament Type cannot be empty"); + return false; + } else { + return true; + } + then: + - lambda: |- + auto message = new nfc::NdefMessage(); + // Application/Json NDEF record + auto json_record = std::make_unique(); + json_record->set_tnf(nfc::TNF_MIME_MEDIA); + json_record->set_type("application/json"); + + DynamicJsonDocument doc(256); // Adjust size as needed + JsonObject root = doc.to(); + root["version"] = "1.0"; + root["protocol"] = "openspool"; + if (id(filament_include_alpha).state == true){ + root["color_hex"] = id(filament_color_hexaa).state; + } + else{ + root["color_hex"] = id(filament_color_hex).state; + } + root["type"] = id(filament_type).state; + root["min_temp"] = id(filament_min_temp).state; + root["max_temp"] = id(filament_max_temp).state; + root["brand"] = id(filament_brand).state; + + std::string json_string; + serializeJson(root, json_string); + if (json_string.empty()) { + ESP_LOGE("rfid", "Failed to serialize JSON"); + return; + } + ESP_LOGI("rfid", "JSON content to be written: %s", json_string.c_str()); + json_record->set_payload(json_string); + message->add_record(std::move(json_record)); + + std::vector encoded_message = message->encode(); + size_t message_size = encoded_message.size(); + ESP_LOGI("rfid", "NDEF message size: %zu bytes", message_size); + + id(rfid_reader_spi_0).write_mode(message); + ESP_LOGI("rfid", "Writing JSON NDEF message to tag"); + - wait_until: + not: + pn532.is_writing: + id: rfid_reader_spi_0 + - logger.log: + tag: "rfid" + format: "Finished writing tag" + level: INFO + - lambda: |- + id(set_led_green).execute(id(selected_slot)); + else: + - lambda: |- + ESP_LOGI("rfid", "NFC Tag not present"); diff --git a/firmware/conf.d/web_server.yaml b/firmware/conf.d/web_server.yaml index 33e4257..269a632 100644 --- a/firmware/conf.d/web_server.yaml +++ b/firmware/conf.d/web_server.yaml @@ -6,18 +6,21 @@ web_server: version: 3 local: true sorting_groups: - - id: sorting_group_filament_settings - name: "Filament Settings" - sorting_weight: -50 - - id: sorting_group_rfid - name: "RFID Programming" - sorting_weight: -40 - - id: sorting_group_printer_settings - name: "Printer Settings" - sorting_weight: -30 - - id: sorting_group_info - name: "Information" - sorting_weight: -20 - - id: sorting_group_extra - name: "Extra" - sorting_weight: -10 + - id: sorting_group_filament_settings + name: "Filament Settings" + sorting_weight: -50 + - id: sorting_group_rfid + name: "RFID Programming" + sorting_weight: -40 + - id: sorting_group_printer_settings + name: "Printer Settings" + sorting_weight: -30 + - id: sorting_group_ams + name: "AMS" + sorting_weight: -25 + - id: sorting_group_info + name: "Information" + sorting_weight: -20 + - id: sorting_group_extra + name: "Extra" + sorting_weight: -10 diff --git a/firmware/esp32-s3-super-mini-manual-ams.yaml b/firmware/esp32-s3-super-mini-manual-ams.yaml new file mode 100644 index 0000000..edc1ffd --- /dev/null +++ b/firmware/esp32-s3-super-mini-manual-ams.yaml @@ -0,0 +1,28 @@ +esp32: + board: esp32-s3-devkitc-1 + +substitutions: + hide_ams_sensors: "true" + led_pin: GPIO9 + neopixel_pin: "48" #some boards have this on pin 38 + spi2_type: SPI2 + spi2_clk_pin: GPIO10 + spi2_miso_pin: GPIO11 + spi2_mosi_pin: GPIO12 + encoder_pin_a: GPIO6 + encoder_pin_b: GPIO7 + + rfid0_spi_interface: SPI2 + rfid0_ss_pin: GPIO13 + +packages: + improv-serial: !include conf.d/improv-serial.yaml + bambu_ams: !include conf.d/bambu_ams.yaml + openspool-manual-ams: !include openspool-manual-ams.yaml + led-internal: !include conf.d/led-internal.yaml + extra: !include conf.d/extra.yaml + button: !include conf.d/button.yaml + +dashboard_import: + package_import_url: github://spuder/openspool/firmware/esp32_s3_super_mini.yaml@main + import_full_config: false diff --git a/firmware/openspool-ams.yaml b/firmware/openspool-ams.yaml index 20b20ac..84e17e0 100644 --- a/firmware/openspool-ams.yaml +++ b/firmware/openspool-ams.yaml @@ -1,5 +1,5 @@ - packages: base: !include common.yaml psram: !include conf.d/psram-esp32s3.yaml - pn_532_rfid-ams: !include conf.d/pn532_rfid-ams.yaml \ No newline at end of file + pn_532_rfid-solo: !include conf.d/pn532_rfid-solo.yaml + pn_532_rfid-ams: !include conf.d/pn532_rfid-ams.yaml diff --git a/firmware/openspool-manual-ams.yaml b/firmware/openspool-manual-ams.yaml new file mode 100644 index 0000000..29c8085 --- /dev/null +++ b/firmware/openspool-manual-ams.yaml @@ -0,0 +1,4 @@ +packages: + base: !include common-manual-ams.yaml + psram: !include conf.d/psram-esp32s3.yaml + pn_532_rfid-manual-ams: !include conf.d/pn532_rfid-manual-ams.yaml diff --git a/firmware/openspool-mini.yaml b/firmware/openspool-mini.yaml index 8861db1..0ed44b4 100644 --- a/firmware/openspool-mini.yaml +++ b/firmware/openspool-mini.yaml @@ -1,3 +1,3 @@ - packages: - base: !include common.yaml \ No newline at end of file + base: !include common.yaml + pn532_rfid-solo: !include conf.d/pn532_rfid-solo.yaml From e9ec93527fa66469285f9926844de05f9cf3b21a Mon Sep 17 00:00:00 2001 From: Ember Light Date: Mon, 16 Jun 2025 13:18:24 +0200 Subject: [PATCH 02/22] Add better external LED functions --- firmware/conf.d/led-external.yaml | 343 ++++++++++++++++-------------- 1 file changed, 185 insertions(+), 158 deletions(-) diff --git a/firmware/conf.d/led-external.yaml b/firmware/conf.d/led-external.yaml index e2da575..df36923 100644 --- a/firmware/conf.d/led-external.yaml +++ b/firmware/conf.d/led-external.yaml @@ -1,162 +1,189 @@ --- substitutions: - led_count: '9' + led_count: "17" # Match the max_value from filament_slots light: -- platform: esp32_rmt_led_strip - name: LEDs - id: neopixel_light - state_topic: - pin: ${led_pin} - num_leds: ${led_count} - rgb_order: GRB - chipset: WS2812 - default_transition_length: 0.4s - restore_mode: RESTORE_DEFAULT_ON - entity_category: diagnostic - icon: mdi:led-strip - effects: - - addressable_rainbow: - name: Rainbow - speed: 25 - width: 15 - - addressable_lambda: - name: Breathing Blue - update_interval: 10ms - lambda: |- - static float b = 0; - b = (sin(millis() / 500.0) + 1.0) / 2.0 * 0.6 + 0.4; - auto color = esphome::light::ESPColor(0, 0, uint8_t(255 * b)); - it.all() = color; - - addressable_lambda: - name: Apple Breathing - update_interval: 10ms - lambda: |- - const float PI = 3.14159265359; - //TODO: 42.546 should be replaced by 83.333 - float t = millis() / 2000.0; - float brightness_float = (exp(sin(t * PI)) - 0.368) * 42.546; - uint8_t brightness = uint8_t(brightness_float); - auto color = esphome::light::ESPColor(brightness, 0, 0); - for (int i = 0; i < it.size(); ++i) { - it[i] = color; - } - # - strobe: - # name: "Data Upload" - # colors: - # - state: TRUE - # brightness: 100% - # duration: 50ms - # red: 0% - # green: 100% - # blue: 0% - # - state: FALSE - # duration: 50ms + - platform: esp32_rmt_led_strip + name: LEDs + id: neopixel_light + state_topic: + pin: ${led_pin} + num_leds: ${led_count} + rgb_order: GRB + chipset: WS2812 + default_transition_length: 0.4s + restore_mode: RESTORE_DEFAULT_ON + entity_category: diagnostic + icon: mdi:led-strip + effects: + - addressable_rainbow: + name: Rainbow + speed: 25 + width: 15 + - addressable_lambda: + name: Breathing Blue + update_interval: 10ms + lambda: |- + static float b = 0; + b = (sin(millis() / 500.0) + 1.0) / 2.0 * 0.6 + 0.4; + auto color = esphome::light::ESPColor(0, 0, uint8_t(255 * b)); + it.all() = color; + - addressable_lambda: + name: Apple Breathing + update_interval: 10ms + lambda: |- + const float PI = 3.14159265359; + //TODO: 42.546 should be replaced by 83.333 + float t = millis() / 2000.0; + float brightness_float = (exp(sin(t * PI)) - 0.368) * 42.546; + uint8_t brightness = uint8_t(brightness_float); + auto color = esphome::light::ESPColor(brightness, 0, 0); + for (int i = 0; i < it.size(); ++i) { + it[i] = color; + } + # - strobe: + # name: "Data Upload" + # colors: + # - state: TRUE + # brightness: 100% + # duration: 50ms + # red: 0% + # green: 100% + # blue: 0% + # - state: FALSE + # duration: 50ms script: -- id: set_led_red - parameters: - led_number: int8_t - then: - - light.addressable_set: - id: neopixel_light - color_brightness: 100% - range_from: !lambda "return led_number < 0 ? 0 : led_number;" - range_to: !lambda "return led_number < 0 ? ${led_count} : led_number;" - red: 100% - green: 0% - blue: 0% -- id: set_led_green - parameters: - led_number: int8_t - then: - - light.addressable_set: - id: neopixel_light - color_brightness: 100% - range_from: !lambda "return led_number < 0 ? 0 : led_number;" - range_to: !lambda "return led_number < 0 ? ${led_count} : led_number;" - red: 0% - green: 100% - blue: 0% -- id: set_led_blue - parameters: - led_number: int8_t - then: - - light.addressable_set: - id: neopixel_light - color_brightness: 100% - range_from: !lambda "return led_number < 0 ? 0 : led_number;" - range_to: !lambda "return led_number < 0 ? ${led_count} : led_number;" - red: 0% - green: 0% - blue: 100% -- id: set_led_yellow - parameters: - led_number: int8_t - then: - - light.addressable_set: - id: neopixel_light - color_brightness: 100% - range_from: !lambda "return led_number < 0 ? 0 : led_number;" - range_to: !lambda "return led_number < 0 ? ${led_count} : led_number;" - red: 100% - green: 100% - blue: 0% -- id: set_led_white - parameters: - led_number: int8_t - then: - - light.addressable_set: - id: neopixel_light - color_brightness: 50% - range_from: !lambda "return led_number < 0 ? 0 : led_number;" - range_to: !lambda "return led_number < 0 ? ${led_count} : led_number;" - red: 100% - green: 100% - blue: 100% -- id: set_all_leds_white - then: - - light.turn_on: - id: neopixel_light - effect: none - brightness: 50% - red: 100% - green: 100% - blue: 100% -- id: set_led_off - then: - - light.turn_on: - id: neopixel_light - brightness: 0% - red: 0% - green: 0% - blue: 0% - effect: none - - delay: 50ms - - light.turn_off: - id: neopixel_light - transition_length: 0s -- id: set_led_rainbow - then: - - light.turn_on: - id: neopixel_light - effect: Rainbow -- id: set_led_breathing_blue - then: - - light.turn_on: - id: neopixel_light - effect: none - - light.turn_on: - id: neopixel_light - effect: Breathing Blue - brightness: 100% -- id: set_led_breathing_green - then: - - light.turn_on: - id: neopixel_light - effect: Breathing Green - brightness: 100% -- id: set_led_apple_breathing - then: - - light.turn_on: - id: neopixel_light - effect: Apple Breathing - brightness: 100% + - id: set_led_red + parameters: + led_number: int8_t + then: + - light.addressable_set: + id: neopixel_light + color_brightness: 50% + range_from: !lambda "return led_number < 0 ? 0 : led_number;" + range_to: !lambda "return led_number < 0 ? ${led_count} : led_number;" + red: 100% + green: 0% + blue: 0% + - id: set_led_green + parameters: + led_number: int8_t + then: + - light.addressable_set: + id: neopixel_light + color_brightness: 50% + range_from: !lambda "return led_number < 0 ? 0 : led_number;" + range_to: !lambda "return led_number < 0 ? ${led_count} : led_number;" + red: 0% + green: 100% + blue: 0% + - id: set_led_blue + parameters: + led_number: int8_t + then: + - light.addressable_set: + id: neopixel_light + color_brightness: 50% + range_from: !lambda "return led_number < 0 ? 0 : led_number;" + range_to: !lambda "return led_number < 0 ? ${led_count} : led_number;" + red: 0% + green: 0% + blue: 100% + - id: set_led_cyan + parameters: + led_number: int8_t + then: + - light.addressable_set: + id: neopixel_light + color_brightness: 50% + range_from: !lambda "return led_number < 0 ? 0 : led_number;" + range_to: !lambda "return led_number < 0 ? ${led_count} : led_number;" + red: 0% + green: 100% + blue: 100% + - id: set_led_magenta + parameters: + led_number: int8_t + then: + - light.addressable_set: + id: neopixel_light + color_brightness: 50% + range_from: !lambda "return led_number < 0 ? 0 : led_number;" + range_to: !lambda "return led_number < 0 ? ${led_count} : led_number;" + red: 100% + green: 0% + blue: 100% + - id: set_led_yellow + parameters: + led_number: int8_t + then: + - light.addressable_set: + id: neopixel_light + color_brightness: 50% + range_from: !lambda "return led_number < 0 ? 0 : led_number;" + range_to: !lambda "return led_number < 0 ? ${led_count} : led_number;" + red: 100% + green: 100% + blue: 0% + - id: set_led_white + parameters: + led_number: int8_t + then: + - light.addressable_set: + id: neopixel_light + color_brightness: 50% + range_from: !lambda "return led_number < 0 ? 0 : led_number;" + range_to: !lambda "return led_number < 0 ? ${led_count} : led_number;" + red: 100% + green: 100% + blue: 100% + - id: set_led_off + parameters: + led_number: int8_t + then: + - light.addressable_set: + id: neopixel_light + range_from: !lambda "return led_number < 0 ? 0 : led_number;" + range_to: !lambda "return led_number < 0 ? ${led_count} : led_number;" + red: 0% + green: 0% + blue: 0% + - id: set_leds_white + then: + - light.turn_on: + id: neopixel_light + effect: none + brightness: 50% + red: 100% + green: 100% + blue: 100% + - id: set_leds_off + then: + - light.turn_off: + id: neopixel_light + transition_length: 0s + - id: set_led_rainbow + then: + - light.turn_on: + id: neopixel_light + effect: Rainbow + - id: set_led_breathing_blue + then: + - light.turn_on: + id: neopixel_light + effect: none + - light.turn_on: + id: neopixel_light + effect: Breathing Blue + brightness: 100% + - id: set_led_breathing_green + then: + - light.turn_on: + id: neopixel_light + effect: Breathing Green + brightness: 100% + - id: set_led_apple_breathing + then: + - light.turn_on: + id: neopixel_light + effect: Apple Breathing + brightness: 100% From be94eb579fd7b95ce1d05b85ac6a8dc2d6a04e5f Mon Sep 17 00:00:00 2001 From: Ember Light Date: Mon, 16 Jun 2025 14:08:09 +0200 Subject: [PATCH 03/22] Update package_import_url --- firmware/esp32-s3-super-mini-manual-ams.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/esp32-s3-super-mini-manual-ams.yaml b/firmware/esp32-s3-super-mini-manual-ams.yaml index edc1ffd..2b5f5a7 100644 --- a/firmware/esp32-s3-super-mini-manual-ams.yaml +++ b/firmware/esp32-s3-super-mini-manual-ams.yaml @@ -24,5 +24,5 @@ packages: button: !include conf.d/button.yaml dashboard_import: - package_import_url: github://spuder/openspool/firmware/esp32_s3_super_mini.yaml@main + package_import_url: github://spuder/openspool/firmware/esp32-s3-super-mini-manual-ams.yaml@main import_full_config: false From e220a9aca45b3b3dc334cfa662fd2ba38f38852f Mon Sep 17 00:00:00 2001 From: Ember Light Date: Mon, 16 Jun 2025 22:39:42 +0200 Subject: [PATCH 04/22] Reset the encoder number to 0 on each boot. --- firmware/conf.d/pn532_rfid-manual-ams.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/firmware/conf.d/pn532_rfid-manual-ams.yaml b/firmware/conf.d/pn532_rfid-manual-ams.yaml index d15aadb..6b34a59 100644 --- a/firmware/conf.d/pn532_rfid-manual-ams.yaml +++ b/firmware/conf.d/pn532_rfid-manual-ams.yaml @@ -55,6 +55,7 @@ sensor: id: encoder_sensor name: "Encoder Position" resolution: 2 + restore_mode: ALWAYS_ZERO pin_a: number: ${encoder_pin_a} mode: From 575d157ce0a74d506be357226fd8601ef96b6369 Mon Sep 17 00:00:00 2001 From: Ember Light Date: Mon, 16 Jun 2025 23:04:59 +0200 Subject: [PATCH 05/22] Change from select component to number component (select contains a bug when decreasing) --- firmware/conf.d/bambu_ams.yaml | 61 ++++++++---------- firmware/conf.d/pn532_rfid-manual-ams.yaml | 74 ++++++---------------- 2 files changed, 46 insertions(+), 89 deletions(-) diff --git a/firmware/conf.d/bambu_ams.yaml b/firmware/conf.d/bambu_ams.yaml index d5581d2..182d045 100644 --- a/firmware/conf.d/bambu_ams.yaml +++ b/firmware/conf.d/bambu_ams.yaml @@ -1,28 +1,27 @@ -select: +number: - platform: template name: "Number of AMS units" id: ams_units optimistic: false restore_value: true icon: mdi:printer-3d-nozzle - options: - - "1" - - "2" - - "3" - - "4" - initial_option: "1" + min_value: 1 + max_value: 4 + step: 1 + initial_value: 1 + mode: box set_action: then: - lambda: |- if (id(bambu_model).state == "A1Mini" || id(bambu_model).state == "A1") { - id(ams_units).publish_state("1"); + id(ams_units).publish_state(1); } else { id(ams_units).publish_state(x); } on_value: then: - lambda: |- - ESP_LOGD("AMS", "AMS units set: %s", x.c_str()); + ESP_LOGD("AMS", "Number of AMS units set: %d", (int)x); web_server: sorting_group_id: sorting_group_printer_settings sorting_weight: 100 @@ -33,28 +32,27 @@ select: optimistic: false restore_value: true icon: mdi:printer-3d-nozzle - options: - - "0" - - "1" - - "2" - - "3" - - "4" - initial_option: "1" + min_value: 0 + max_value: 4 + step: 1 + initial_value: 1 + mode: box set_action: then: - lambda: |- if (id(bambu_model).state == "A1Mini" || id(bambu_model).state == "A1") { - id(ams_number).publish_state("1"); + id(ams_number).publish_state(1); } else { - if (x == "0") { - id(slot_number).publish_state("1"); + if (x > id(ams_units).state) { + id(ams_number).publish_state(id(ams_units).state); + } else { + id(ams_number).publish_state(x); } - id(ams_number).publish_state(x); } on_value: then: - lambda: |- - ESP_LOGD("AMS", "AMS selected: %s", x.c_str()); + ESP_LOGD("AMS", "AMS selected: %d", (int)x); web_server: sorting_group_id: sorting_group_ams @@ -64,27 +62,22 @@ select: optimistic: false restore_value: true icon: mdi:printer-3d-nozzle - options: - - "1" - - "2" - - "3" - - "4" - initial_option: "1" + min_value: 1 + max_value: 4 + step: 1 + initial_value: 1 + mode: box set_action: then: - lambda: |- - if (id(ams_number).state == "0") { - // Only force to 1 if manually selecting through UI, not from encoder - if (x != id(slot_number).state) { - id(slot_number).publish_state("1"); - ESP_LOGD("AMS", "Forcing slot to 1 because AMS is 0"); - } + if (id(ams_number).state == 0) { + id(slot_number).publish_state(1); } else { id(slot_number).publish_state(x); } on_value: then: - lambda: |- - ESP_LOGD("AMS", "Slot selected: %s", x.c_str()); + ESP_LOGD("AMS", "Slot selected: %d", (int)x); web_server: sorting_group_id: sorting_group_ams diff --git a/firmware/conf.d/pn532_rfid-manual-ams.yaml b/firmware/conf.d/pn532_rfid-manual-ams.yaml index 6b34a59..5917e03 100644 --- a/firmware/conf.d/pn532_rfid-manual-ams.yaml +++ b/firmware/conf.d/pn532_rfid-manual-ams.yaml @@ -13,9 +13,7 @@ script: - id: update_leds then: - lambda: |- - auto ams_index = id(ams_number).active_index(); - auto slot_index = id(slot_number).active_index(); - for (int i = 0; i < ams_index.value() * slot_index.value(); i++) { + for (int i = 0; i < id(ams_units).state * 4; i++) { if (i == id(selected_slot)) { id(set_led_blue).execute(i); } else { @@ -43,9 +41,9 @@ script: uint16_t ams_id = 255; // Default to external AMS uint16_t tray_id = 254; // Default to external tray - if (id(ams_number).active_index().value() > 0) { - ams_id = id(ams_number).active_index().value(); - tray_id = id(slot_number).active_index().value(); + if (id(ams_number).state > 0) { + ams_id = id(ams_number).state - 1; + tray_id = id(slot_number).state - 1; } return bambulabs::generate_mqtt_payload(payload_data, ams_id, tray_id); @@ -69,66 +67,32 @@ sensor: on_clockwise: then: - lambda: |- - if(id(ams_number).state == "0") { - id(ams_number).publish_state("1"); - } else if (id(slot_number).state == "4") { + if(id(ams_number).state == 0) { + id(ams_number).publish_state(1); + } else if (id(slot_number).state == 4) { if (id(ams_number).state == id(ams_units).state) { - id(ams_number).publish_state("0"); + id(ams_number).publish_state(0); } else { - auto cur_index = id(ams_number).active_index().value(); - auto option = id(ams_number).at(cur_index + 1).value(); - id(ams_number).publish_state(option.c_str()); + id(ams_number).publish_state(id(ams_number).state + 1); } - id(slot_number).publish_state("1"); + id(slot_number).publish_state(1); } else { - // Increment the slot directly using the current state value - int current_slot = std::stoi(id(slot_number).state); - int next_slot = current_slot + 1; - // Make sure it stays in valid range - if (next_slot > 4) next_slot = 4; - id(slot_number).publish_state(std::to_string(next_slot).c_str()); - - // Debugging output - ESP_LOGD("Encoder", "Incrementing slot from %d to %d", current_slot, next_slot); - } - // Force state and UI update - if (id(slot_number).state == "3" || id(slot_number).state == "4") { - // This bypasses normal mechanisms to force the UI update - id(slot_number).state = id(slot_number).state; - id(slot_number).publish_state(id(slot_number).state); + id(slot_number).publish_state(id(slot_number).state + 1); } - script.execute: update_leds on_anticlockwise: then: - lambda: |- - if (id(ams_number).state == "0") { - // If at AMS 0, go to max AMS and max slot - id(ams_number).publish_state(id(ams_units).state.c_str()); - id(slot_number).publish_state("4"); - } else if (id(slot_number).state == "1") { - // If at slot 1, go to previous AMS and max slot - auto cur_ams = id(ams_number).state; - if (cur_ams == "1") { - // If at AMS 1, go to AMS 0 - id(ams_number).publish_state("0"); - id(slot_number).publish_state("1"); - } else { - // Otherwise, go to previous AMS and max slot - int prev_ams = std::stoi(cur_ams) - 1; - id(ams_number).publish_state(std::to_string(prev_ams).c_str()); - id(slot_number).publish_state("4"); - } + if(id(ams_number).state == 0) { + id(ams_number).publish_state(id(ams_units).state); + id(slot_number).publish_state(4); + } else if (id(slot_number).state == 1) { + id(ams_number).publish_state(id(ams_number).state - 1); + id(slot_number).publish_state(4); } else { - // Just decrement the slot - but do it directly using the current state value - int current_slot = std::stoi(id(slot_number).state); - int prev_slot = current_slot - 1; - // Make sure it stays in valid range - if (prev_slot < 1) prev_slot = 1; - id(slot_number).publish_state(std::to_string(prev_slot).c_str()); - - // Debugging output - ESP_LOGD("Encoder", "Decrementing slot from %d to %d", current_slot, prev_slot); + id(slot_number).publish_state(id(slot_number).state - 1); } + - script.execute: update_leds binary_sensor: From e2dd3aba972158907dc42911e6d67a3933e5207e Mon Sep 17 00:00:00 2001 From: Ember Light Date: Tue, 17 Jun 2025 00:44:40 +0200 Subject: [PATCH 06/22] Fix selected_slot and led selection --- firmware/common-manual-ams.yaml | 3 ++- firmware/conf.d/bambu_ams.yaml | 29 ++++++++++++++++++++-- firmware/conf.d/led-external.yaml | 21 ++++++++-------- firmware/conf.d/pn532_rfid-manual-ams.yaml | 19 ++------------ 4 files changed, 41 insertions(+), 31 deletions(-) diff --git a/firmware/common-manual-ams.yaml b/firmware/common-manual-ams.yaml index 31c6178..a73f36c 100644 --- a/firmware/common-manual-ams.yaml +++ b/firmware/common-manual-ams.yaml @@ -65,7 +65,8 @@ esphome: # id(my_ota).set_auth_password("New password"); on_shutdown: then: - - script.execute: set_leds_off + - light.turn_off: + id: neopixel_light esp32: framework: type: esp-idf diff --git a/firmware/conf.d/bambu_ams.yaml b/firmware/conf.d/bambu_ams.yaml index 182d045..16eb4ea 100644 --- a/firmware/conf.d/bambu_ams.yaml +++ b/firmware/conf.d/bambu_ams.yaml @@ -1,3 +1,20 @@ +globals: + - id: selected_slot + type: int + restore_value: yes + initial_value: "0" + +script: + - id: update_selected_slot + then: + - lambda: |- + if (id(ams_number).state == 0) { + id(selected_slot) = 0; + } else { + id(selected_slot) = (id(ams_number).state - 1) * 4 + id(slot_number).state; + } + - script.execute: update_leds + number: - platform: template name: "Number of AMS units" @@ -9,7 +26,6 @@ number: max_value: 4 step: 1 initial_value: 1 - mode: box set_action: then: - lambda: |- @@ -36,7 +52,6 @@ number: max_value: 4 step: 1 initial_value: 1 - mode: box set_action: then: - lambda: |- @@ -49,10 +64,15 @@ number: id(ams_number).publish_state(x); } } + - script.execute: update_selected_slot on_value: then: - lambda: |- ESP_LOGD("AMS", "AMS selected: %d", (int)x); + // If AMS is 0, ensure slot is forced to 1 + if (x == 0 && id(slot_number).state != 1) { + id(slot_number).publish_state(1); + } web_server: sorting_group_id: sorting_group_ams @@ -75,9 +95,14 @@ number: } else { id(slot_number).publish_state(x); } + - script.execute: update_selected_slot on_value: then: - lambda: |- ESP_LOGD("AMS", "Slot selected: %d", (int)x); + // If AMS is 0, ensure slot is forced to 1 + if (id(ams_number).state == 0 && x != 1) { + id(slot_number).publish_state(1); + } web_server: sorting_group_id: sorting_group_ams diff --git a/firmware/conf.d/led-external.yaml b/firmware/conf.d/led-external.yaml index df36923..1147c4b 100644 --- a/firmware/conf.d/led-external.yaml +++ b/firmware/conf.d/led-external.yaml @@ -52,13 +52,22 @@ light: # - state: FALSE # duration: 50ms script: + - id: update_leds + then: + - lambda: |- + // Clear all LEDs first + for (int i = 0; i < 17; i++) { + id(set_led_off).execute(i); + } + ESP_LOGD("LED", "LED index: %d", (int)id(selected_slot)); + //id(set_leds_off).execute(); + id(set_led_blue).execute(id(selected_slot)); - id: set_led_red parameters: led_number: int8_t then: - light.addressable_set: id: neopixel_light - color_brightness: 50% range_from: !lambda "return led_number < 0 ? 0 : led_number;" range_to: !lambda "return led_number < 0 ? ${led_count} : led_number;" red: 100% @@ -70,7 +79,6 @@ script: then: - light.addressable_set: id: neopixel_light - color_brightness: 50% range_from: !lambda "return led_number < 0 ? 0 : led_number;" range_to: !lambda "return led_number < 0 ? ${led_count} : led_number;" red: 0% @@ -82,7 +90,6 @@ script: then: - light.addressable_set: id: neopixel_light - color_brightness: 50% range_from: !lambda "return led_number < 0 ? 0 : led_number;" range_to: !lambda "return led_number < 0 ? ${led_count} : led_number;" red: 0% @@ -94,7 +101,6 @@ script: then: - light.addressable_set: id: neopixel_light - color_brightness: 50% range_from: !lambda "return led_number < 0 ? 0 : led_number;" range_to: !lambda "return led_number < 0 ? ${led_count} : led_number;" red: 0% @@ -106,7 +112,6 @@ script: then: - light.addressable_set: id: neopixel_light - color_brightness: 50% range_from: !lambda "return led_number < 0 ? 0 : led_number;" range_to: !lambda "return led_number < 0 ? ${led_count} : led_number;" red: 100% @@ -118,7 +123,6 @@ script: then: - light.addressable_set: id: neopixel_light - color_brightness: 50% range_from: !lambda "return led_number < 0 ? 0 : led_number;" range_to: !lambda "return led_number < 0 ? ${led_count} : led_number;" red: 100% @@ -130,7 +134,6 @@ script: then: - light.addressable_set: id: neopixel_light - color_brightness: 50% range_from: !lambda "return led_number < 0 ? 0 : led_number;" range_to: !lambda "return led_number < 0 ? ${led_count} : led_number;" red: 100% @@ -152,7 +155,6 @@ script: - light.turn_on: id: neopixel_light effect: none - brightness: 50% red: 100% green: 100% blue: 100% @@ -174,16 +176,13 @@ script: - light.turn_on: id: neopixel_light effect: Breathing Blue - brightness: 100% - id: set_led_breathing_green then: - light.turn_on: id: neopixel_light effect: Breathing Green - brightness: 100% - id: set_led_apple_breathing then: - light.turn_on: id: neopixel_light effect: Apple Breathing - brightness: 100% diff --git a/firmware/conf.d/pn532_rfid-manual-ams.yaml b/firmware/conf.d/pn532_rfid-manual-ams.yaml index 5917e03..ec0f89a 100644 --- a/firmware/conf.d/pn532_rfid-manual-ams.yaml +++ b/firmware/conf.d/pn532_rfid-manual-ams.yaml @@ -3,23 +3,7 @@ substitutions: spi_data_rate: 200kHz update_interval: "750ms" -globals: - - id: selected_slot - type: int - restore_value: yes - initial_value: "0" - script: - - id: update_leds - then: - - lambda: |- - for (int i = 0; i < id(ams_units).state * 4; i++) { - if (i == id(selected_slot)) { - id(set_led_blue).execute(i); - } else { - id(set_led_off).execute(i); - } - } - id: publish_filament_data_to_mqtt parameters: payload_data: string @@ -79,7 +63,7 @@ sensor: } else { id(slot_number).publish_state(id(slot_number).state + 1); } - - script.execute: update_leds + - script.execute: update_selected_slot on_anticlockwise: then: - lambda: |- @@ -94,6 +78,7 @@ sensor: } - script.execute: update_leds + - script.execute: update_selected_slot binary_sensor: - platform: template From d1ee9adc662c2f3e95fcbe8aca460d709fd6fb15 Mon Sep 17 00:00:00 2001 From: Ember Light Date: Tue, 17 Jun 2025 00:44:58 +0200 Subject: [PATCH 07/22] Update the ams and slot calculation for rotary encoder --- firmware/conf.d/pn532_rfid-manual-ams.yaml | 55 +++++++++++++++------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/firmware/conf.d/pn532_rfid-manual-ams.yaml b/firmware/conf.d/pn532_rfid-manual-ams.yaml index ec0f89a..cf70dce 100644 --- a/firmware/conf.d/pn532_rfid-manual-ams.yaml +++ b/firmware/conf.d/pn532_rfid-manual-ams.yaml @@ -51,33 +51,54 @@ sensor: on_clockwise: then: - lambda: |- - if(id(ams_number).state == 0) { - id(ams_number).publish_state(1); - } else if (id(slot_number).state == 4) { - if (id(ams_number).state == id(ams_units).state) { + // Calculate total slots (AMS 0 + all AMS units with 4 slots each) + int total_slots = 1 + id(ams_units).state * 4; + int current_slot; + + // Get current absolute slot position + if (id(ams_number).state == 0) { + current_slot = 0; + } else { + current_slot = (id(ams_number).state - 1) * 4 + id(slot_number).state; + } + + // Move to next slot + current_slot = (current_slot + 1) % total_slots; + + // Convert back to AMS and slot + if (current_slot == 0) { id(ams_number).publish_state(0); - } else { - id(ams_number).publish_state(id(ams_number).state + 1); - } - id(slot_number).publish_state(1); + id(slot_number).publish_state(1); } else { - id(slot_number).publish_state(id(slot_number).state + 1); + id(ams_number).publish_state((current_slot - 1) / 4 + 1); + id(slot_number).publish_state((current_slot - 1) % 4 + 1); } - script.execute: update_selected_slot on_anticlockwise: then: - lambda: |- - if(id(ams_number).state == 0) { - id(ams_number).publish_state(id(ams_units).state); - id(slot_number).publish_state(4); - } else if (id(slot_number).state == 1) { - id(ams_number).publish_state(id(ams_number).state - 1); - id(slot_number).publish_state(4); + // Calculate total slots (AMS 0 + all AMS units with 4 slots each) + int total_slots = 1 + id(ams_units).state * 4; + int current_slot; + + // Get current absolute slot position + if (id(ams_number).state == 0) { + current_slot = 0; } else { - id(slot_number).publish_state(id(slot_number).state - 1); + current_slot = (id(ams_number).state - 1) * 4 + id(slot_number).state; } - - script.execute: update_leds + // Move to previous slot + current_slot = (current_slot + total_slots - 1) % total_slots; + + // Convert back to AMS and slot + if (current_slot == 0) { + id(ams_number).publish_state(0); + id(slot_number).publish_state(1); + } else { + id(ams_number).publish_state((current_slot - 1) / 4 + 1); + id(slot_number).publish_state((current_slot - 1) % 4 + 1); + } - script.execute: update_selected_slot binary_sensor: From 7223e3fd6035905ce935ae1d6de7959f1c226c72 Mon Sep 17 00:00:00 2001 From: Ember Light Date: Wed, 18 Jun 2025 15:41:42 +0200 Subject: [PATCH 08/22] Update esphome to 2025.06.0 --- firmware/common-manual-ams.yaml | 8 +++++--- firmware/conf.d/led-external.yaml | 1 - firmware/conf.d/led-internal.yaml | 24 ++++++++++++------------ 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/firmware/common-manual-ams.yaml b/firmware/common-manual-ams.yaml index a73f36c..1256561 100644 --- a/firmware/common-manual-ams.yaml +++ b/firmware/common-manual-ams.yaml @@ -1,4 +1,6 @@ --- +preferences: + flash_write_interval: 3sec substitutions: name: openspool esphome: @@ -7,7 +9,7 @@ esphome: project: name: spuder.openspool version: ${version} - min_version: 2024.11.0 + min_version: 2025.6.0 platformio_options: build_unflags: -std=gnu++11 build_flags: @@ -70,8 +72,8 @@ esphome: esp32: framework: type: esp-idf - version: 5.3.1 - platform_version: 6.9.0 # https://github.com/platformio/platform-espressif32/releases/ + #version: 5.3.1 + #platform_version: 6.9.0 # https://github.com/platformio/platform-espressif32/releases/ sdkconfig_options: CONFIG_MBEDTLS_HKDF_C: y # Needed for bambu KDF CONFIG_MBEDTLS_MD_C: y # Needed for bambu KDF diff --git a/firmware/conf.d/led-external.yaml b/firmware/conf.d/led-external.yaml index 1147c4b..f077731 100644 --- a/firmware/conf.d/led-external.yaml +++ b/firmware/conf.d/led-external.yaml @@ -5,7 +5,6 @@ light: - platform: esp32_rmt_led_strip name: LEDs id: neopixel_light - state_topic: pin: ${led_pin} num_leds: ${led_count} rgb_order: GRB diff --git a/firmware/conf.d/led-internal.yaml b/firmware/conf.d/led-internal.yaml index 749cb3e..7204928 100644 --- a/firmware/conf.d/led-internal.yaml +++ b/firmware/conf.d/led-internal.yaml @@ -1,14 +1,14 @@ --- light: -- platform: esp32_rmt_led_strip - name: "Internal LED" - id: internal_led - state_topic: - pin: ${neopixel_pin} - chipset: WS2812 - rgb_order: GRB - num_leds: 1 - rmt_symbols: 48 - entity_category: diagnostic - restore_mode: ALWAYS_OFF - icon: mdi:led-strip + - platform: esp32_rmt_led_strip + name: "Internal LED" + id: internal_led + state_topic: + pin: ${neopixel_pin} + chipset: WS2812 + rgb_order: GRB + num_leds: 1 + rmt_symbols: 48 + entity_category: diagnostic + restore_mode: ALWAYS_OFF + icon: mdi:led-strip From ac25681bfd5758018cbf0bece024cd738733685e Mon Sep 17 00:00:00 2001 From: Ember Light Date: Wed, 18 Jun 2025 15:43:25 +0200 Subject: [PATCH 09/22] Renamed update_leds to select_led to reflect better what it's doing --- firmware/common-manual-ams.yaml | 2 +- firmware/conf.d/bambu_ams.yaml | 2 +- firmware/conf.d/led-external.yaml | 17 ++--------------- 3 files changed, 4 insertions(+), 17 deletions(-) diff --git a/firmware/common-manual-ams.yaml b/firmware/common-manual-ams.yaml index 1256561..4ec2e21 100644 --- a/firmware/common-manual-ams.yaml +++ b/firmware/common-manual-ams.yaml @@ -37,7 +37,7 @@ esphome: - light.turn_off: id: neopixel_light - delay: 500ms - - script.execute: update_leds + - script.execute: select_led - if: condition: lambda: |- diff --git a/firmware/conf.d/bambu_ams.yaml b/firmware/conf.d/bambu_ams.yaml index 16eb4ea..ef7180e 100644 --- a/firmware/conf.d/bambu_ams.yaml +++ b/firmware/conf.d/bambu_ams.yaml @@ -13,7 +13,7 @@ script: } else { id(selected_slot) = (id(ams_number).state - 1) * 4 + id(slot_number).state; } - - script.execute: update_leds + - script.execute: select_led number: - platform: template diff --git a/firmware/conf.d/led-external.yaml b/firmware/conf.d/led-external.yaml index f077731..67eeaa7 100644 --- a/firmware/conf.d/led-external.yaml +++ b/firmware/conf.d/led-external.yaml @@ -39,28 +39,15 @@ light: for (int i = 0; i < it.size(); ++i) { it[i] = color; } - # - strobe: - # name: "Data Upload" - # colors: - # - state: TRUE - # brightness: 100% - # duration: 50ms - # red: 0% - # green: 100% - # blue: 0% - # - state: FALSE - # duration: 50ms script: - - id: update_leds + - id: select_led then: - lambda: |- - // Clear all LEDs first for (int i = 0; i < 17; i++) { id(set_led_off).execute(i); } - ESP_LOGD("LED", "LED index: %d", (int)id(selected_slot)); - //id(set_leds_off).execute(); id(set_led_blue).execute(id(selected_slot)); + ESP_LOGD("LED", "LED index: %d", (int)id(selected_slot)); - id: set_led_red parameters: led_number: int8_t From c01f27d9f0201fe1f8434aa8c6fa7ad27a18350a Mon Sep 17 00:00:00 2001 From: Ember Light Date: Wed, 18 Jun 2025 15:44:17 +0200 Subject: [PATCH 10/22] Move rotary encoder code to its own file --- firmware/conf.d/pn532_rfid-manual-ams.yaml | 69 ------------------ .../conf.d/rotary-encoder-manual-ams.yaml | 72 +++++++++++++++++++ firmware/esp32-s3-super-mini-manual-ams.yaml | 1 + 3 files changed, 73 insertions(+), 69 deletions(-) create mode 100644 firmware/conf.d/rotary-encoder-manual-ams.yaml diff --git a/firmware/conf.d/pn532_rfid-manual-ams.yaml b/firmware/conf.d/pn532_rfid-manual-ams.yaml index cf70dce..1bf42ba 100644 --- a/firmware/conf.d/pn532_rfid-manual-ams.yaml +++ b/firmware/conf.d/pn532_rfid-manual-ams.yaml @@ -32,75 +32,6 @@ script: return bambulabs::generate_mqtt_payload(payload_data, ams_id, tray_id); -sensor: - - platform: rotary_encoder - id: encoder_sensor - name: "Encoder Position" - resolution: 2 - restore_mode: ALWAYS_ZERO - pin_a: - number: ${encoder_pin_a} - mode: - input: true - pullup: true - pin_b: - number: ${encoder_pin_b} - mode: - input: true - pullup: true - on_clockwise: - then: - - lambda: |- - // Calculate total slots (AMS 0 + all AMS units with 4 slots each) - int total_slots = 1 + id(ams_units).state * 4; - int current_slot; - - // Get current absolute slot position - if (id(ams_number).state == 0) { - current_slot = 0; - } else { - current_slot = (id(ams_number).state - 1) * 4 + id(slot_number).state; - } - - // Move to next slot - current_slot = (current_slot + 1) % total_slots; - - // Convert back to AMS and slot - if (current_slot == 0) { - id(ams_number).publish_state(0); - id(slot_number).publish_state(1); - } else { - id(ams_number).publish_state((current_slot - 1) / 4 + 1); - id(slot_number).publish_state((current_slot - 1) % 4 + 1); - } - - script.execute: update_selected_slot - on_anticlockwise: - then: - - lambda: |- - // Calculate total slots (AMS 0 + all AMS units with 4 slots each) - int total_slots = 1 + id(ams_units).state * 4; - int current_slot; - - // Get current absolute slot position - if (id(ams_number).state == 0) { - current_slot = 0; - } else { - current_slot = (id(ams_number).state - 1) * 4 + id(slot_number).state; - } - - // Move to previous slot - current_slot = (current_slot + total_slots - 1) % total_slots; - - // Convert back to AMS and slot - if (current_slot == 0) { - id(ams_number).publish_state(0); - id(slot_number).publish_state(1); - } else { - id(ams_number).publish_state((current_slot - 1) / 4 + 1); - id(slot_number).publish_state((current_slot - 1) % 4 + 1); - } - - script.execute: update_selected_slot - binary_sensor: - platform: template name: "NFC Tag Present" diff --git a/firmware/conf.d/rotary-encoder-manual-ams.yaml b/firmware/conf.d/rotary-encoder-manual-ams.yaml new file mode 100644 index 0000000..e00afc4 --- /dev/null +++ b/firmware/conf.d/rotary-encoder-manual-ams.yaml @@ -0,0 +1,72 @@ +script: + - id: navigate_slots + parameters: + direction: int # 1 for clockwise, -1 for anticlockwise + then: + - lambda: |- + // Calculate total slots (AMS 0 + all AMS units with 4 slots each) + int total_slots = 1 + id(ams_units).state * 4; + int current_slot; + + // Get current absolute slot position + if (id(ams_number).state == 0) { + current_slot = 0; + } else { + current_slot = (id(ams_number).state - 1) * 4 + id(slot_number).state; + } + + // Move to next/previous slot based on direction + if (direction > 0) { + current_slot = (current_slot + 1) % total_slots; + } else { + current_slot = (current_slot + total_slots - 1) % total_slots; + } + + // Convert back to AMS and slot + auto slot_call = id(slot_number).make_call(); + auto ams_call = id(ams_number).make_call(); + if (current_slot == 0) { + slot_call.set_value(1); + ams_call.set_value(0); + slot_call.perform(); + ams_call.perform(); + } else { + int ams = (current_slot - 1) / 4 + 1; + int slot = (current_slot - 1) % 4 + 1; + if (id(ams_number).state != ams) { + ams_call.set_value(ams); + ams_call.perform(); + } + if (id(slot_number).state != slot) { + slot_call.set_value(slot); + slot_call.perform(); + } + } + - script.execute: update_selected_slot + +sensor: + - platform: rotary_encoder + id: encoder_sensor + name: "Encoder Position" + resolution: 2 + restore_mode: ALWAYS_ZERO + pin_a: + number: ${encoder_pin_a} + mode: + input: true + pullup: true + pin_b: + number: ${encoder_pin_b} + mode: + input: true + pullup: true + on_clockwise: + then: + - script.execute: + id: navigate_slots + direction: 1 + on_anticlockwise: + then: + - script.execute: + id: navigate_slots + direction: -1 diff --git a/firmware/esp32-s3-super-mini-manual-ams.yaml b/firmware/esp32-s3-super-mini-manual-ams.yaml index 2b5f5a7..1f48335 100644 --- a/firmware/esp32-s3-super-mini-manual-ams.yaml +++ b/firmware/esp32-s3-super-mini-manual-ams.yaml @@ -19,6 +19,7 @@ packages: improv-serial: !include conf.d/improv-serial.yaml bambu_ams: !include conf.d/bambu_ams.yaml openspool-manual-ams: !include openspool-manual-ams.yaml + encoder: !include conf.d/rotary-encoder-manual-ams.yaml # Comment out if you don't use the rotary encoder led-internal: !include conf.d/led-internal.yaml extra: !include conf.d/extra.yaml button: !include conf.d/button.yaml From 3ef2aeb532e5a1f498d2c38b31b4f910716e6c7e Mon Sep 17 00:00:00 2001 From: Ember Light Date: Wed, 18 Jun 2025 15:46:36 +0200 Subject: [PATCH 11/22] Fix conditions when manually selecting units, ams and slots in web UI --- firmware/conf.d/bambu_ams.yaml | 51 ++++++++++++++++------------------ 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/firmware/conf.d/bambu_ams.yaml b/firmware/conf.d/bambu_ams.yaml index ef7180e..a5195ef 100644 --- a/firmware/conf.d/bambu_ams.yaml +++ b/firmware/conf.d/bambu_ams.yaml @@ -1,7 +1,7 @@ globals: - id: selected_slot type: int - restore_value: yes + restore_value: true initial_value: "0" script: @@ -22,7 +22,7 @@ number: optimistic: false restore_value: true icon: mdi:printer-3d-nozzle - min_value: 1 + min_value: 0 max_value: 4 step: 1 initial_value: 1 @@ -30,14 +30,23 @@ number: then: - lambda: |- if (id(bambu_model).state == "A1Mini" || id(bambu_model).state == "A1") { + ESP_LOGD("AMS Units", "A1Mini or A1 detected, setting AMS to 1"); id(ams_units).publish_state(1); } else { + ESP_LOGD("AMS Units", "AMS units: %d", (int)id(ams_units).state); id(ams_units).publish_state(x); } - on_value: - then: - - lambda: |- ESP_LOGD("AMS", "Number of AMS units set: %d", (int)x); + + if (x < id(ams_number).state) { + ESP_LOGD("AMS Units", "AMS is higher than current units size, forcing AMS to %d", (int)x); + id(ams_number).publish_state(x); + } + if (x == 0) { + ESP_LOGD("AMS Units", "AMS units is 0, forcing slot to 1"); + id(slot_number).publish_state(1); + } + - script.execute: update_selected_slot web_server: sorting_group_id: sorting_group_printer_settings sorting_weight: 100 @@ -55,24 +64,19 @@ number: set_action: then: - lambda: |- - if (id(bambu_model).state == "A1Mini" || id(bambu_model).state == "A1") { - id(ams_number).publish_state(1); + if (x > id(ams_units).state) { + ESP_LOGD("ams_number", "AMS number too high, setting to %d", (int)id(ams_units).state); + id(ams_number).publish_state(id(ams_units).state); } else { - if (x > id(ams_units).state) { - id(ams_number).publish_state(id(ams_units).state); - } else { - id(ams_number).publish_state(x); - } + id(ams_number).publish_state(x); } - - script.execute: update_selected_slot - on_value: - then: - - lambda: |- ESP_LOGD("AMS", "AMS selected: %d", (int)x); - // If AMS is 0, ensure slot is forced to 1 + if (x == 0 && id(slot_number).state != 1) { - id(slot_number).publish_state(1); + ESP_LOGD("AMS", "AMS is 0, forcing slot to 1"); + id(slot_number).publish_state(1); } + - script.execute: update_selected_slot web_server: sorting_group_id: sorting_group_ams @@ -86,23 +90,16 @@ number: max_value: 4 step: 1 initial_value: 1 - mode: box set_action: then: - lambda: |- if (id(ams_number).state == 0) { + ESP_LOGD("slot_number", "AMS is 0, forcing slot to 1"); id(slot_number).publish_state(1); } else { id(slot_number).publish_state(x); } - - script.execute: update_selected_slot - on_value: - then: - - lambda: |- ESP_LOGD("AMS", "Slot selected: %d", (int)x); - // If AMS is 0, ensure slot is forced to 1 - if (id(ams_number).state == 0 && x != 1) { - id(slot_number).publish_state(1); - } + - script.execute: update_selected_slot web_server: sorting_group_id: sorting_group_ams From d5c4fb0a8153d7018c31051829c6ea74e5f528c5 Mon Sep 17 00:00:00 2001 From: Ember Light Date: Wed, 18 Jun 2025 15:47:19 +0200 Subject: [PATCH 12/22] reorder substitutions for better clarity --- firmware/esp32-s3-super-mini-manual-ams.yaml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/firmware/esp32-s3-super-mini-manual-ams.yaml b/firmware/esp32-s3-super-mini-manual-ams.yaml index 1f48335..3c3b79f 100644 --- a/firmware/esp32-s3-super-mini-manual-ams.yaml +++ b/firmware/esp32-s3-super-mini-manual-ams.yaml @@ -2,17 +2,15 @@ esp32: board: esp32-s3-devkitc-1 substitutions: - hide_ams_sensors: "true" - led_pin: GPIO9 - neopixel_pin: "48" #some boards have this on pin 38 spi2_type: SPI2 + rfid0_spi_interface: SPI2 + neopixel_pin: GPIO21 + encoder_pin_a: GPIO6 + encoder_pin_b: GPIO7 + led_pin: GPIO9 spi2_clk_pin: GPIO10 spi2_miso_pin: GPIO11 spi2_mosi_pin: GPIO12 - encoder_pin_a: GPIO6 - encoder_pin_b: GPIO7 - - rfid0_spi_interface: SPI2 rfid0_ss_pin: GPIO13 packages: From 7411828df29f011e9aa0107b2e6c31c82db43352 Mon Sep 17 00:00:00 2001 From: Ember Light Date: Wed, 18 Jun 2025 16:20:18 +0200 Subject: [PATCH 13/22] remove the import of button.yaml as it's not used. --- firmware/esp32-s3-super-mini-manual-ams.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/firmware/esp32-s3-super-mini-manual-ams.yaml b/firmware/esp32-s3-super-mini-manual-ams.yaml index 3c3b79f..129864c 100644 --- a/firmware/esp32-s3-super-mini-manual-ams.yaml +++ b/firmware/esp32-s3-super-mini-manual-ams.yaml @@ -20,7 +20,6 @@ packages: encoder: !include conf.d/rotary-encoder-manual-ams.yaml # Comment out if you don't use the rotary encoder led-internal: !include conf.d/led-internal.yaml extra: !include conf.d/extra.yaml - button: !include conf.d/button.yaml dashboard_import: package_import_url: github://spuder/openspool/firmware/esp32-s3-super-mini-manual-ams.yaml@main From f230ea89b8fb168f33157835fb830a90e7597096 Mon Sep 17 00:00:00 2001 From: Ember Light Date: Wed, 18 Jun 2025 18:10:49 +0200 Subject: [PATCH 14/22] Clean up comments --- firmware/common-manual-ams.yaml | 25 +++---------------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/firmware/common-manual-ams.yaml b/firmware/common-manual-ams.yaml index 4ec2e21..0de7617 100644 --- a/firmware/common-manual-ams.yaml +++ b/firmware/common-manual-ams.yaml @@ -1,8 +1,10 @@ --- preferences: flash_write_interval: 3sec + substitutions: name: openspool + esphome: name: ${name} name_add_mac_suffix: true @@ -56,38 +58,17 @@ esphome: format: "Missing Bambu Credentials, skipping mqtt connect" - mqtt.disable: id: bambu_mqtt - # - script.execute: set_all_leds_white - # - lambda: |- - # //TODO: this appears to have broken and no longer blinks blue - # if (!wifi::global_wifi_component->is_connected() && - # wifi::global_wifi_component->wifi_soft_ap_ip().str() == "192.168.4.1") { - # id(set_led_breathing_blue).execute(); - # } - # - lambda: |- - # id(my_ota).set_auth_password("New password"); on_shutdown: then: - light.turn_off: id: neopixel_light + esp32: framework: type: esp-idf - #version: 5.3.1 - #platform_version: 6.9.0 # https://github.com/platformio/platform-espressif32/releases/ sdkconfig_options: CONFIG_MBEDTLS_HKDF_C: y # Needed for bambu KDF CONFIG_MBEDTLS_MD_C: y # Needed for bambu KDF - # version: recommended - # sdkconfig_options: - # MBEDTLS_CERTIFICATE_BUNDLE: y - # MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL: y - -#TODO: uncomment when ready for bambu support -# external_components: -# - source: -# type: local -# path: components -# components: [pn532, nfc] packages: version: !include conf.d/version.yaml From ac5220c201ca76b347697b33cfe73765c171ed39 Mon Sep 17 00:00:00 2001 From: Ember Light Date: Wed, 18 Jun 2025 18:11:19 +0200 Subject: [PATCH 15/22] Add correct flash size and psram info for esp32 S3 super mini --- firmware/esp32-s3-super-mini-manual-ams.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/firmware/esp32-s3-super-mini-manual-ams.yaml b/firmware/esp32-s3-super-mini-manual-ams.yaml index 129864c..9caba53 100644 --- a/firmware/esp32-s3-super-mini-manual-ams.yaml +++ b/firmware/esp32-s3-super-mini-manual-ams.yaml @@ -1,5 +1,10 @@ esp32: board: esp32-s3-devkitc-1 + flash_size: 4MB + +psram: + mode: quad + speed: 80MHz substitutions: spi2_type: SPI2 From 54570a928ffa51eb7c287bd083a34b07dada4704 Mon Sep 17 00:00:00 2001 From: Ember Light Date: Wed, 18 Jun 2025 18:11:46 +0200 Subject: [PATCH 16/22] Correct LED pins and name --- firmware/conf.d/led-external.yaml | 3 ++- firmware/esp32-s3-super-mini-manual-ams.yaml | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/firmware/conf.d/led-external.yaml b/firmware/conf.d/led-external.yaml index 67eeaa7..4c66c39 100644 --- a/firmware/conf.d/led-external.yaml +++ b/firmware/conf.d/led-external.yaml @@ -5,8 +5,9 @@ light: - platform: esp32_rmt_led_strip name: LEDs id: neopixel_light - pin: ${led_pin} + pin: ${external_led_pin} num_leds: ${led_count} + rmt_symbols: 48 rgb_order: GRB chipset: WS2812 default_transition_length: 0.4s diff --git a/firmware/esp32-s3-super-mini-manual-ams.yaml b/firmware/esp32-s3-super-mini-manual-ams.yaml index 9caba53..4dae1d2 100644 --- a/firmware/esp32-s3-super-mini-manual-ams.yaml +++ b/firmware/esp32-s3-super-mini-manual-ams.yaml @@ -9,10 +9,10 @@ psram: substitutions: spi2_type: SPI2 rfid0_spi_interface: SPI2 - neopixel_pin: GPIO21 + neopixel_pin: GPIO48 encoder_pin_a: GPIO6 encoder_pin_b: GPIO7 - led_pin: GPIO9 + external_led_pin: GPIO9 spi2_clk_pin: GPIO10 spi2_miso_pin: GPIO11 spi2_mosi_pin: GPIO12 From 72c318094f012a3db1396106dc736862738dc1e0 Mon Sep 17 00:00:00 2001 From: Ember Light Date: Wed, 18 Jun 2025 18:14:32 +0200 Subject: [PATCH 17/22] Place encoder info in diagnostic category --- firmware/conf.d/rotary-encoder-manual-ams.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/firmware/conf.d/rotary-encoder-manual-ams.yaml b/firmware/conf.d/rotary-encoder-manual-ams.yaml index e00afc4..12dd036 100644 --- a/firmware/conf.d/rotary-encoder-manual-ams.yaml +++ b/firmware/conf.d/rotary-encoder-manual-ams.yaml @@ -70,3 +70,4 @@ sensor: - script.execute: id: navigate_slots direction: -1 + entity_category: diagnostic From 22dc30d630ded40c6803dcc763bba7e2df4ceac5 Mon Sep 17 00:00:00 2001 From: Ember Light Date: Wed, 18 Jun 2025 18:15:35 +0200 Subject: [PATCH 18/22] Correct external led name in web ui --- firmware/conf.d/led-external.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/conf.d/led-external.yaml b/firmware/conf.d/led-external.yaml index 4c66c39..c0dd888 100644 --- a/firmware/conf.d/led-external.yaml +++ b/firmware/conf.d/led-external.yaml @@ -3,7 +3,7 @@ substitutions: led_count: "17" # Match the max_value from filament_slots light: - platform: esp32_rmt_led_strip - name: LEDs + name: External LEDs id: neopixel_light pin: ${external_led_pin} num_leds: ${led_count} From edb037580ec53f3ddb8c98e2989fb9b8c82ab830 Mon Sep 17 00:00:00 2001 From: Ember Light Date: Wed, 18 Jun 2025 18:48:01 +0200 Subject: [PATCH 19/22] Increase rmt_symbols for external led to reduce flickering --- firmware/conf.d/led-external.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/conf.d/led-external.yaml b/firmware/conf.d/led-external.yaml index c0dd888..2deb36b 100644 --- a/firmware/conf.d/led-external.yaml +++ b/firmware/conf.d/led-external.yaml @@ -7,7 +7,7 @@ light: id: neopixel_light pin: ${external_led_pin} num_leds: ${led_count} - rmt_symbols: 48 + rmt_symbols: 144 rgb_order: GRB chipset: WS2812 default_transition_length: 0.4s From 70b304f7df4bcb1c48382a3ffeb419daeee253cc Mon Sep 17 00:00:00 2001 From: Ember Light Date: Wed, 18 Jun 2025 18:48:19 +0200 Subject: [PATCH 20/22] Change ams icons for better match --- firmware/conf.d/bambu_ams.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/firmware/conf.d/bambu_ams.yaml b/firmware/conf.d/bambu_ams.yaml index a5195ef..9abdc06 100644 --- a/firmware/conf.d/bambu_ams.yaml +++ b/firmware/conf.d/bambu_ams.yaml @@ -21,7 +21,7 @@ number: id: ams_units optimistic: false restore_value: true - icon: mdi:printer-3d-nozzle + icon: mdi:train-car-container min_value: 0 max_value: 4 step: 1 @@ -56,7 +56,7 @@ number: id: ams_number optimistic: false restore_value: true - icon: mdi:printer-3d-nozzle + icon: mdi:train-car-container min_value: 0 max_value: 4 step: 1 @@ -85,7 +85,7 @@ number: id: slot_number optimistic: false restore_value: true - icon: mdi:printer-3d-nozzle + icon: mdi:record-circle-outline min_value: 1 max_value: 4 step: 1 From 49730e0faf1f512061592d0676c81596b7ab0523 Mon Sep 17 00:00:00 2001 From: Ember Light Date: Wed, 18 Jun 2025 21:14:00 +0200 Subject: [PATCH 21/22] Set cpu frequency lower to reduce power consumption --- firmware/esp32-s3-super-mini-manual-ams.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/firmware/esp32-s3-super-mini-manual-ams.yaml b/firmware/esp32-s3-super-mini-manual-ams.yaml index 4dae1d2..6268796 100644 --- a/firmware/esp32-s3-super-mini-manual-ams.yaml +++ b/firmware/esp32-s3-super-mini-manual-ams.yaml @@ -1,6 +1,7 @@ esp32: board: esp32-s3-devkitc-1 flash_size: 4MB + cpu_frequency: 80MHz # Lowest possible frequency to reduce power consumption psram: mode: quad From 2d40c855e704bdc41a8df710331ccd897324dd9b Mon Sep 17 00:00:00 2001 From: Ember Light Date: Wed, 18 Jun 2025 23:37:54 +0200 Subject: [PATCH 22/22] Add LED Brightness slider to be able to set the LED brightness and color brightness from UI --- firmware/common-manual-ams.yaml | 1 - firmware/conf.d/led-external.yaml | 24 ++++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/firmware/common-manual-ams.yaml b/firmware/common-manual-ams.yaml index 0de7617..a95f74a 100644 --- a/firmware/common-manual-ams.yaml +++ b/firmware/common-manual-ams.yaml @@ -23,7 +23,6 @@ esphome: - light.turn_on: id: neopixel_light effect: Breathing Blue - brightness: 100% - wait_until: condition: wifi.connected: diff --git a/firmware/conf.d/led-external.yaml b/firmware/conf.d/led-external.yaml index 2deb36b..cf98124 100644 --- a/firmware/conf.d/led-external.yaml +++ b/firmware/conf.d/led-external.yaml @@ -1,6 +1,30 @@ --- substitutions: led_count: "17" # Match the max_value from filament_slots + +number: + - platform: template + name: "LED Brightness" + id: led_brightness + icon: mdi:led-on + entity_category: config + min_value: 1 + max_value: 100 + step: 1 + initial_value: 50 + unit_of_measurement: "%" + optimistic: true + restore_value: true + on_value: + then: + - lambda: |- + auto call = id(neopixel_light).make_call(); + call.set_brightness(x / 100.0); + call.set_color_brightness(x / 100.0); + call.perform(); + - delay: 500ms + - script.execute: select_led + light: - platform: esp32_rmt_led_strip name: External LEDs