{
  "version": 1,
  "source": "tools/features.toml",
  "groups": [
    {
      "id": "network",
      "order": 1,
      "advanced": false,
      "label": {
        "en": "Network",
        "pt": "Rede"
      }
    },
    {
      "id": "telemetry",
      "order": 2,
      "advanced": false,
      "label": {
        "en": "Telemetry and integrations",
        "pt": "Telemetria e integrações"
      }
    },
    {
      "id": "interface",
      "order": 3,
      "advanced": false,
      "label": {
        "en": "Interface",
        "pt": "Interface"
      }
    },
    {
      "id": "sensors",
      "order": 4,
      "advanced": false,
      "label": {
        "en": "Sensors",
        "pt": "Sensores"
      }
    },
    {
      "id": "sound",
      "order": 5,
      "advanced": false,
      "label": {
        "en": "Sound",
        "pt": "Som"
      }
    },
    {
      "id": "power",
      "order": 6,
      "advanced": false,
      "label": {
        "en": "Power",
        "pt": "Energia"
      }
    },
    {
      "id": "bench",
      "order": 7,
      "advanced": true,
      "label": {
        "en": "Bench and diagnostics",
        "pt": "Bancada e diagnóstico"
      }
    }
  ],
  "toggles": [
    {
      "id": "cli_full",
      "group": "interface",
      "bit": 0,
      "label": {
        "en": "Full command line",
        "pt": "Linha de comando completa"
      },
      "help": {
        "en": "Every serial and Bluetooth command. Without it only the emergency console remains.",
        "pt": "Todos os comandos por serial e Bluetooth. Sem ela fica só o console de emergência."
      }
    },
    {
      "id": "web_https",
      "group": "network",
      "bit": 1,
      "label": {
        "en": "HTTPS web server",
        "pt": "Servidor web HTTPS"
      },
      "help": {
        "en": "Serves the web interface over TLS. Without it the device answers plain HTTP only.",
        "pt": "Serve a interface web por TLS. Sem ele o aparelho responde só em HTTP."
      }
    },
    {
      "id": "license_stub",
      "group": "bench",
      "bit": 2,
      "label": {
        "en": "Short license text on the touch display",
        "pt": "Texto curto da licença na tela touch"
      },
      "help": {
        "en": "The touch display's License screen shows two lines that point to the LICENSE file instead of the full MIT text and credits. The /license web page always keeps the full text.",
        "pt": "A tela de Licença do mostrador touch mostra duas linhas que apontam para o arquivo LICENSE, em vez do texto MIT completo e dos créditos. A página web /license mantém sempre o texto completo."
      }
    },
    {
      "id": "mdns",
      "group": "network",
      "bit": 3,
      "label": {
        "en": "Find by name on the network (mDNS)",
        "pt": "Achar pelo nome na rede (mDNS)"
      },
      "help": {
        "en": "Answers to <device name>.local, so a browser finds it without the IP address.",
        "pt": "Responde por <nome do aparelho>.local, para o navegador achá-lo sem o endereço IP."
      }
    },
    {
      "id": "concurrency_asserts",
      "group": "bench",
      "bit": 4,
      "label": {
        "en": "Concurrency tripwire",
        "pt": "Armadilha de concorrência"
      },
      "help": {
        "en": "Logs an error when a flash operation starts while the state mutex is held. For soak runs, not for field images.",
        "pt": "Registra um erro quando uma operação de flash começa com o mutex de estado preso. Para soak, não para campo."
      }
    },
    {
      "id": "bluetooth",
      "group": "network",
      "bit": 5,
      "label": {
        "en": "Bluetooth serial console",
        "pt": "Console serial por Bluetooth"
      },
      "help": {
        "en": "A password-protected console over classic Bluetooth (SPP). It advertises for the first 5 minutes after power-up, so it reaches a device whose Wi-Fi is not set up yet.",
        "pt": "Um console protegido por senha, por Bluetooth clássico (SPP). Ele se anuncia nos primeiros 5 minutos depois de ligar e assim alcança um aparelho com o Wi-Fi ainda por configurar."
      }
    },
    {
      "id": "air",
      "group": "power",
      "bit": 6,
      "label": {
        "en": "Hibernation between readings",
        "pt": "Hibernação entre leituras"
      },
      "help": {
        "en": "The Air cycle: wake, read, send, sleep. Exists only without a display.",
        "pt": "O ciclo do Air: acorda, lê, envia, dorme. Só existe sem mostrador."
      }
    },
    {
      "id": "sound_buzzer",
      "group": "sound",
      "bit": 7,
      "label": {
        "en": "Buzzer sounds",
        "pt": "Sons no buzzer"
      },
      "help": {
        "en": "Touch clicks, confirmations and alarm sounds on the buzzer (GP22).",
        "pt": "Cliques de toque, confirmações e sons de alarme no buzzer (GP22)."
      }
    },
    {
      "id": "sensor_ds18b20",
      "group": "sensors",
      "bit": 8,
      "label": {
        "en": "DS18B20 temperature probes",
        "pt": "Sondas de temperatura DS18B20"
      },
      "help": {
        "en": "1-Wire probes, one per pin, each recognized by its serial number.",
        "pt": "Sondas 1-Wire, uma por pino, cada uma reconhecida pelo número de série."
      }
    },
    {
      "id": "sensor_dht22",
      "group": "sensors",
      "bit": 9,
      "label": {
        "en": "DHT22 temperature and humidity",
        "pt": "DHT22 temperatura e umidade"
      },
      "help": {
        "en": "One sensor per pin.",
        "pt": "Um sensor por pino."
      }
    },
    {
      "id": "sensor_bme280",
      "group": "sensors",
      "bit": 10,
      "label": {
        "en": "BME280 / BMP280 temperature, humidity and pressure",
        "pt": "BME280 / BMP280 temperatura, umidade e pressão"
      },
      "help": {
        "en": "I2C sensor on a pin pair; the BMP280 variant has no humidity.",
        "pt": "Sensor I2C num par de pinos; a variante BMP280 não mede umidade."
      }
    },
    {
      "id": "tel_tls",
      "group": "telemetry",
      "bit": 11,
      "label": {
        "en": "Encrypted telemetry (HTTPS and MQTTS)",
        "pt": "Telemetria criptografada (HTTPS e MQTTS)"
      },
      "help": {
        "en": "Sends to the collector over TLS, and validates it against /cert.pem when one is uploaded. Without it telemetry goes over plain HTTP or MQTT only, and a configuration that asks for encryption is refused instead of being sent in the clear.",
        "pt": "Envia ao coletor por TLS, e o valida contra o /cert.pem quando há um. Sem ela a telemetria vai só por HTTP ou MQTT sem criptografia, e uma configuração que pede criptografia é recusada em vez de ir em claro."
      }
    },
    {
      "id": "tel_mqtt",
      "group": "telemetry",
      "bit": 12,
      "label": {
        "en": "MQTT and Home Assistant",
        "pt": "MQTT e Home Assistant"
      },
      "help": {
        "en": "Publishes to an MQTT broker (MQTT, or MQTTS with the TLS switch), takes the alarm line's receipts on the ack topic, and announces the sensors to Home Assistant by MQTT Discovery. Without it telemetry goes by HTTP only.",
        "pt": "Publica num broker MQTT (MQTT, ou MQTTS com a chave de TLS), recebe as confirmações da linha de alarmes no tópico de ack e anuncia os sensores ao Home Assistant pelo MQTT Discovery. Sem ele a telemetria vai só por HTTP."
      }
    },
    {
      "id": "web_history",
      "group": "interface",
      "bit": 13,
      "label": {
        "en": "History page in the web interface",
        "pt": "Página de histórico na interface web"
      },
      "help": {
        "en": "The /history page: charts, calendar, CSV export in the browser and the event log viewer. The device records history either way, and the history files stay downloadable from the Files page.",
        "pt": "A página /history: gráficos, calendário, CSV no navegador e o visor de eventos. O aparelho grava o histórico do mesmo jeito, e os arquivos de histórico continuam na página Arquivos."
      }
    },
    {
      "id": "web_export_api",
      "group": "interface",
      "bit": 14,
      "label": {
        "en": "History export API",
        "pt": "API de exportação do histórico"
      },
      "help": {
        "en": "Routes that return several sensors' history in one call (/api/history_multi) and export the history and the log as .simx. No page uses them; bench tools and integrations do.",
        "pt": "Rotas que devolvem o histórico de vários sensores numa chamada (/api/history_multi) e exportam o histórico e o log em .simx. Nenhuma página as usa; ferramentas de bancada e integrações, sim."
      }
    },
    {
      "id": "web_metrics",
      "group": "telemetry",
      "bit": 15,
      "label": {
        "en": "Prometheus metrics (/metrics)",
        "pt": "Métricas para o Prometheus (/metrics)"
      },
      "help": {
        "en": "A /metrics endpoint in the Prometheus text format, with Basic authentication for the scraper.",
        "pt": "Um endpoint /metrics no formato de texto do Prometheus, com autenticação Basic para o coletor."
      }
    },
    {
      "id": "syslog",
      "group": "telemetry",
      "bit": 16,
      "label": {
        "en": "Remote syslog (audit trail)",
        "pt": "Syslog remoto (trilha de auditoria)"
      },
      "help": {
        "en": "Forwards log events to a syslog collector (RFC 5424 over UDP), so the audit trail survives outside the device.",
        "pt": "Encaminha os eventos do log a um coletor syslog (RFC 5424 por UDP), para a trilha de auditoria sobreviver fora do aparelho."
      }
    },
    {
      "id": "tft_graph",
      "group": "interface",
      "bit": 17,
      "label": {
        "en": "Graph and calendar on the touch display",
        "pt": "Gráfico e calendário na tela touch"
      },
      "help": {
        "en": "The history graph opened from the min/max strip, its detail screen and the calendar to pick a day. Without it the strip has no graph button; the history is still recorded and stays on the web.",
        "pt": "O gráfico do histórico aberto pela faixa de min/max, a tela de detalhe e o calendário para escolher o dia. Sem ele a faixa não tem o botão do gráfico; o histórico continua gravado e na web."
      }
    }
  ],
  "displays": {
    "tft": {
      "label": {
        "en": "Touch display (ILI9341, 320x240)",
        "pt": "Tela touch (ILI9341, 320x240)"
      }
    },
    "alpha_paralelo": {
      "label": {
        "en": "16x2 character LCD (HD44780)",
        "pt": "LCD de caracteres 16x2 (HD44780)"
      }
    },
    "nenhum": {
      "label": {
        "en": "No display",
        "pt": "Sem mostrador"
      }
    }
  },
  "net": {
    "group": "network",
    "label": {
      "en": "Connection",
      "pt": "Conexão"
    },
    "options": [
      {
        "id": "cyw43",
        "class": "wifi",
        "label": {
          "en": "Wi-Fi (the board's CYW43 radio)",
          "pt": "Wi-Fi (o rádio CYW43 da placa)"
        },
        "help": {
          "en": "The radio of the Pico W and the Pico 2 W: the device joins a network by its name and password, and offers a setup access point.",
          "pt": "O rádio do Pico W e do Pico 2 W: o aparelho entra numa rede pelo nome e pela senha, e oferece um ponto de acesso para a configuração."
        }
      },
      {
        "id": "enc28j60",
        "class": "lan",
        "label": {
          "en": "Network cable (ENC28J60 module)",
          "pt": "Cabo de rede (módulo ENC28J60)"
        },
        "help": {
          "en": "Builds for a plain Raspberry Pi Pico, which has no radio, with an ENC28J60 module on GP11 to GP15: sensor slots go from 16 to 11. Bluetooth and hibernation run on the radio, so they are not available.",
          "pt": "Compila para um Raspberry Pi Pico comum, que não tem rádio, com um módulo ENC28J60 nos GP11 a GP15: os slots de sensor caem de 16 para 11. O Bluetooth e a hibernação rodam no rádio, então não ficam disponíveis."
        }
      }
    ]
  },
  "products": [
    {
      "id": "pico_w_release",
      "order": 1,
      "bench": false,
      "publish": true,
      "chip": "rp2040",
      "refused": {},
      "ceilings": {
        "flash_region": 1044480,
        "ota_bin": 1040143
      },
      "label": {
        "en": "SIMUT",
        "pt": "SIMUT"
      },
      "desc": {
        "en": "Touch display, web interface over HTTPS, sensors, alarms and telemetry.",
        "pt": "Tela touch, interface web por HTTPS, sensores, alarmes e telemetria."
      },
      "config": {
        "display": "tft",
        "net": "cyw43",
        "cli_full": false,
        "web_https": true,
        "license_stub": false,
        "mdns": true,
        "concurrency_asserts": false,
        "bluetooth": false,
        "air": false,
        "sound_buzzer": true,
        "sensor_ds18b20": true,
        "sensor_dht22": true,
        "sensor_bme280": true,
        "tel_tls": true,
        "tel_mqtt": true,
        "web_history": true,
        "web_export_api": true,
        "web_metrics": true,
        "syslog": true,
        "tft_graph": true
      }
    },
    {
      "id": "pico_w_alpha",
      "order": 2,
      "bench": false,
      "publish": true,
      "chip": "rp2040",
      "refused": {},
      "ceilings": {
        "flash_region": 1044480,
        "ota_bin": 1040143
      },
      "label": {
        "en": "SIMUT Alpha",
        "pt": "SIMUT Alpha"
      },
      "desc": {
        "en": "16x2 character LCD instead of the touch display, and a Bluetooth console.",
        "pt": "LCD de caracteres 16x2 no lugar da tela touch, e um console por Bluetooth."
      },
      "config": {
        "display": "alpha_paralelo",
        "net": "cyw43",
        "cli_full": false,
        "web_https": false,
        "license_stub": false,
        "mdns": false,
        "concurrency_asserts": false,
        "bluetooth": true,
        "air": false,
        "sound_buzzer": true,
        "sensor_ds18b20": true,
        "sensor_dht22": true,
        "sensor_bme280": true,
        "tel_tls": true,
        "tel_mqtt": true,
        "web_history": true,
        "web_export_api": true,
        "web_metrics": true,
        "syslog": true,
        "tft_graph": false
      }
    },
    {
      "id": "pico_w_air",
      "order": 3,
      "bench": false,
      "publish": true,
      "chip": "rp2040",
      "refused": {},
      "ceilings": {
        "flash_region": 1044480,
        "ota_bin": 1040143
      },
      "label": {
        "en": "SIMUT Air",
        "pt": "SIMUT Air"
      },
      "desc": {
        "en": "No display: wakes, reads, sends and sleeps again, to run on battery.",
        "pt": "Sem mostrador: acorda, lê, envia e volta a dormir, para rodar na bateria."
      },
      "config": {
        "display": "nenhum",
        "net": "cyw43",
        "cli_full": true,
        "web_https": false,
        "license_stub": true,
        "mdns": false,
        "concurrency_asserts": false,
        "bluetooth": true,
        "air": true,
        "sound_buzzer": false,
        "sensor_ds18b20": true,
        "sensor_dht22": true,
        "sensor_bme280": true,
        "tel_tls": true,
        "tel_mqtt": true,
        "web_history": true,
        "web_export_api": true,
        "web_metrics": true,
        "syslog": true,
        "tft_graph": false
      }
    },
    {
      "id": "pico2_w_release",
      "order": 4,
      "bench": false,
      "publish": true,
      "chip": "rp2350",
      "refused": {
        "net=enc28j60": {
          "en": "The ENC28J60 exists only for the plain Pico (RP2040) so far: the Pico 2 W (RP2350) has no board for it.",
          "pt": "O ENC28J60 só existe, por enquanto, para o Pico comum (RP2040): o Pico 2 W (RP2350) não tem placa para ele."
        }
      },
      "ceilings": {
        "flash_region": 1568768,
        "ota_bin": 1568527
      },
      "label": {
        "en": "SIMUT for the Pico 2 W",
        "pt": "SIMUT para o Pico 2 W"
      },
      "desc": {
        "en": "The touch display product on the Pico 2 W (RP2350), with twice the flash: it boots from slot A or B, and an update over the air goes into the other slot and runs on trial.",
        "pt": "O produto com tela touch no Pico 2 W (RP2350), com o dobro de flash: sobe do slot A ou B, e uma atualização pelo ar vai para o outro slot e roda em teste."
      },
      "config": {
        "display": "tft",
        "net": "cyw43",
        "cli_full": false,
        "web_https": true,
        "license_stub": false,
        "mdns": true,
        "concurrency_asserts": false,
        "bluetooth": false,
        "air": false,
        "sound_buzzer": true,
        "sensor_ds18b20": true,
        "sensor_dht22": true,
        "sensor_bme280": true,
        "tel_tls": true,
        "tel_mqtt": true,
        "web_history": true,
        "web_export_api": true,
        "web_metrics": true,
        "syslog": true,
        "tft_graph": true
      }
    },
    {
      "id": "pico_w_test",
      "order": 5,
      "bench": true,
      "publish": false,
      "chip": "rp2040",
      "refused": {},
      "ceilings": {
        "flash_region": 1044480,
        "ota_bin": 1040143
      },
      "label": {
        "en": "Bench image",
        "pt": "Imagem de bancada"
      },
      "desc": {
        "en": "Full command line for the test suites. Not published.",
        "pt": "Linha de comando completa para as suítes de teste. Não publicada."
      },
      "config": {
        "display": "tft",
        "net": "cyw43",
        "cli_full": true,
        "web_https": false,
        "license_stub": true,
        "mdns": false,
        "concurrency_asserts": false,
        "bluetooth": false,
        "air": false,
        "sound_buzzer": true,
        "sensor_ds18b20": true,
        "sensor_dht22": true,
        "sensor_bme280": true,
        "tel_tls": true,
        "tel_mqtt": true,
        "web_history": true,
        "web_export_api": true,
        "web_metrics": true,
        "syslog": true,
        "tft_graph": true
      }
    },
    {
      "id": "pico_w_test_https",
      "order": 6,
      "bench": true,
      "publish": false,
      "chip": "rp2040",
      "refused": {},
      "ceilings": {
        "flash_region": 1044480,
        "ota_bin": 1040143
      },
      "label": {
        "en": "Bench image with HTTPS",
        "pt": "Imagem de bancada com HTTPS"
      },
      "desc": {
        "en": "The only image with the full command line and the TLS server together. Not published.",
        "pt": "A única imagem com linha de comando completa e servidor TLS juntos. Não publicada."
      },
      "config": {
        "display": "tft",
        "net": "cyw43",
        "cli_full": true,
        "web_https": true,
        "license_stub": true,
        "mdns": false,
        "concurrency_asserts": false,
        "bluetooth": false,
        "air": false,
        "sound_buzzer": true,
        "sensor_ds18b20": true,
        "sensor_dht22": true,
        "sensor_bme280": true,
        "tel_tls": true,
        "tel_mqtt": true,
        "web_history": true,
        "web_export_api": true,
        "web_metrics": true,
        "syslog": true,
        "tft_graph": true
      }
    },
    {
      "id": "pico_w_asserts",
      "order": 7,
      "bench": true,
      "publish": false,
      "chip": "rp2040",
      "refused": {},
      "ceilings": {
        "flash_region": 1044480,
        "ota_bin": 1040143
      },
      "label": {
        "en": "Concurrency soak image",
        "pt": "Imagem de soak de concorrência"
      },
      "desc": {
        "en": "The release image with the concurrency tripwire armed. Not published.",
        "pt": "A imagem de release com a armadilha de concorrência armada. Não publicada."
      },
      "config": {
        "display": "tft",
        "net": "cyw43",
        "cli_full": false,
        "web_https": true,
        "license_stub": false,
        "mdns": true,
        "concurrency_asserts": true,
        "bluetooth": false,
        "air": false,
        "sound_buzzer": true,
        "sensor_ds18b20": true,
        "sensor_dht22": true,
        "sensor_bme280": true,
        "tel_tls": true,
        "tel_mqtt": true,
        "web_history": true,
        "web_export_api": true,
        "web_metrics": true,
        "syslog": true,
        "tft_graph": true
      }
    },
    {
      "id": "pico_wire",
      "order": 8,
      "bench": true,
      "publish": false,
      "chip": "rp2040",
      "refused": {},
      "ceilings": {
        "flash_region": 1044480,
        "ota_bin": 1040143
      },
      "label": {
        "en": "SIMUT Wire",
        "pt": "SIMUT Wire"
      },
      "desc": {
        "en": "A plain Raspberry Pi Pico with no radio: the network comes through an ENC28J60 module on the cable. Not published.",
        "pt": "Um Raspberry Pi Pico comum, sem rádio: a rede vem por um módulo ENC28J60, no cabo. Não publicada."
      },
      "config": {
        "display": "alpha_paralelo",
        "net": "enc28j60",
        "cli_full": false,
        "web_https": false,
        "license_stub": false,
        "mdns": false,
        "concurrency_asserts": false,
        "bluetooth": false,
        "air": false,
        "sound_buzzer": true,
        "sensor_ds18b20": false,
        "sensor_dht22": true,
        "sensor_bme280": true,
        "tel_tls": true,
        "tel_mqtt": true,
        "web_history": true,
        "web_export_api": true,
        "web_metrics": true,
        "syslog": true,
        "tft_graph": false
      }
    },
    {
      "id": "pico_wiretft",
      "order": 9,
      "bench": true,
      "publish": false,
      "chip": "rp2040",
      "refused": {},
      "ceilings": {
        "flash_region": 1044480,
        "ota_bin": 1040143
      },
      "label": {
        "en": "SIMUT Wire TFT",
        "pt": "SIMUT Wire TFT"
      },
      "desc": {
        "en": "The SIMUT Wire with the touch display: a plain Raspberry Pi Pico with no radio, the network coming through an ENC28J60 module on the cable. Not published.",
        "pt": "O SIMUT Wire com a tela touch: um Raspberry Pi Pico comum, sem rádio, com a rede por um módulo ENC28J60, no cabo. Não publicada."
      },
      "config": {
        "display": "tft",
        "net": "enc28j60",
        "cli_full": false,
        "web_https": true,
        "license_stub": false,
        "mdns": true,
        "concurrency_asserts": false,
        "bluetooth": false,
        "air": false,
        "sound_buzzer": true,
        "sensor_ds18b20": false,
        "sensor_dht22": true,
        "sensor_bme280": true,
        "tel_tls": true,
        "tel_mqtt": true,
        "web_history": true,
        "web_export_api": true,
        "web_metrics": true,
        "syslog": true,
        "tft_graph": true
      }
    }
  ],
  "rules": [
    {
      "id": "air-needs-headless",
      "when": {
        "air": true
      },
      "require": {
        "display": "nenhum"
      },
      "why": {
        "en": "Hibernation needs a build without a display.",
        "pt": "A hibernação exige uma build sem mostrador."
      },
      "evidence": "TFT+air and alpha+air fail to link: multiple definition of DisplayManager::DisplayManager(). SIMUT_AIR switches on DisplayManager_None.cpp, which collides with the chosen display's DisplayManager."
    },
    {
      "id": "headless-needs-air",
      "when": {
        "display": "nenhum"
      },
      "require": {
        "air": true
      },
      "why": {
        "en": "A build without a display exists only with hibernation.",
        "pt": "Uma build sem mostrador só existe com a hibernação."
      },
      "evidence": "The air profile with air=false fails to link: undefined reference to DisplayManager::begin(). The headless display drops DisplayManager.cpp and the non-air boot path calls it."
    },
    {
      "id": "graph-needs-tft",
      "when": {
        "tft_graph": true
      },
      "require": {
        "display": "tft"
      },
      "why": {
        "en": "The graph draws on the touch display.",
        "pt": "O gráfico é desenhado na tela touch."
      },
      "evidence": "SIMUT_TFT_GRAPH follows SIMUT_DISPLAY_TFT in simut_config.h, so an alpha or Air profile with the switch on still comes out without the graph (its three units leave with the display), and forcing -DSIMUT_TFT_GRAPH=1 without the TFT stops at an #error there."
    },
    {
      "id": "air-no-buzzer",
      "when": {
        "air": true
      },
      "require": {
        "sound_buzzer": false
      },
      "why": {
        "en": "The hibernating build carries no buzzer: its sound code does not compile with hibernation on.",
        "pt": "A build que hiberna não leva o buzzer: o código de som não compila com a hibernação ligada."
      },
      "evidence": "air+sound_buzzer fails to compile: SoundManager.cpp: 'BuzzerNote' does not name a type. SIMUT_AIR turns SoundManager.h into the no-op class, and SoundManager.cpp has no guard of its own."
    },
    {
      "id": "bluetooth-needs-cyw43",
      "when": {
        "bluetooth": true
      },
      "require": {
        "net": "cyw43"
      },
      "why": {
        "en": "Bluetooth runs on the CYW43 radio, so it needs the Wi-Fi connection.",
        "pt": "O Bluetooth roda no rádio CYW43, então precisa da conexão Wi-Fi."
      },
      "evidence": "With net = enc28j60, SIMUT_BLUETOOTH stops at an #error in simut_config.h, pinned by tools/test_pin_masks.py (2026-10-12). The test of that #error was SIMUT_NET_LAN && SIMUT_BLUETOOTH, the cable; since 2026-10-13 it is SIMUT_BLUETOOTH && !SIMUT_NET_CYW43, the chip. Not compiled past that #error: the Bluetooth stack (SerialBT, BTstack) rides on the CYW43, and board = rpipico has none. The rule asks for the radio, not for the absence of the cable: a wired board added later has no CYW43 either, and the #error already refuses it."
    },
    {
      "id": "air-needs-cyw43",
      "when": {
        "air": true
      },
      "require": {
        "net": "cyw43"
      },
      "why": {
        "en": "Hibernation is built around the CYW43 radio, which it powers down between wakes, so it needs the Wi-Fi connection.",
        "pt": "A hibernação é feita em torno do rádio CYW43, que ela desliga entre os despertares, então precisa da conexão Wi-Fi."
      },
      "evidence": "With net = enc28j60, SIMUT_AIR stops at an #error in simut_config.h, pinned by tools/test_pin_masks.py (2026-10-12). The test of that #error was SIMUT_NET_LAN && SIMUT_AIR, the cable; since 2026-10-13 it is SIMUT_AIR && !SIMUT_NET_CYW43, the chip. Not compiled past that #error: AppManager_Air.cpp powers the CYW43 down through GPIO23 (WL_REG_ON) before every sleep, and board = rpipico has no CYW43. The rule asks for the radio, not for the absence of the cable: a wired board added later has no CYW43 either, and the #error already refuses it."
    }
  ],
  "hazards": [
    {
      "id": "enc28j60-unsoaked",
      "when": {
        "net": "enc28j60"
      },
      "why": {
        "en": "The ENC28J60 connection has run on the bench, not yet through a soak: a build with it is a bench build.",
        "pt": "A conexão ENC28J60 rodou na bancada, mas ainda não passou por um soak: uma build com ela é de bancada."
      },
      "evidence": "2026-10-13: pico_wiretft on the Wire rig, web and console over the cable for hours; no image with the ENC28J60 has run a 72 h soak."
    },
    {
      "id": "bt-flash-core1",
      "when": {
        "bluetooth": true,
        "display": "tft"
      },
      "why": {
        "en": "Bluetooth writes its pairing data to flash without pausing Core 1, which draws the touch display. Inferred from the framework source, never reproduced.",
        "pt": "O Bluetooth grava os dados de pareamento no flash sem pausar o Core 1, que desenha a tela touch. Inferido do código do framework, nunca reproduzido."
      },
      "evidence": "MODELO_DE_RECURSOS.md R-H24 (inferred): btstack_flash_bank.cpp:53-56 writes the TLV without Core1FlashPause."
    }
  ],
  "budget": [
    {
      "id": "pio-budget",
      "chip": "rp2040",
      "when": {
        "sound_buzzer": false,
        "sensor_ds18b20": true,
        "sensor_dht22": true,
        "net": "enc28j60"
      },
      "why": {
        "en": "This does not fit the PIO blocks of this chip. Turn off one of these: DS18B20 temperature probes; DHT22 temperature and humidity.",
        "pt": "Isto não cabe nos blocos PIO deste chip. Desligue um destes: Sondas de temperatura DS18B20; DHT22 temperatura e umidade."
      },
      "detail": "enc28j60 needs 17 instructions and 1 state machine and finds no block where it leaves room for the programs that load after it: pio0 has 32 instructions and 4 state machines free, but then onewire would not fit; pio1 has 32 instructions and 4 state machines free, but then dht22 would not fit.",
      "fix": [
        "sensor_ds18b20",
        "sensor_dht22"
      ],
      "lock": {
        "sensor_ds18b20": {
          "en": "This does not fit the PIO blocks of this chip. Turn off: DHT22 temperature and humidity.",
          "pt": "Isto não cabe nos blocos PIO deste chip. Desligue: DHT22 temperatura e umidade."
        },
        "sensor_dht22": {
          "en": "This does not fit the PIO blocks of this chip. Turn off: DS18B20 temperature probes.",
          "pt": "Isto não cabe nos blocos PIO deste chip. Desligue: Sondas de temperatura DS18B20."
        }
      }
    },
    {
      "id": "pio-budget",
      "chip": "rp2040",
      "when": {
        "sound_buzzer": true,
        "sensor_ds18b20": true,
        "sensor_dht22": true,
        "net": "enc28j60"
      },
      "why": {
        "en": "This does not fit the PIO blocks of this chip. Turn off one of these: DS18B20 temperature probes; DHT22 temperature and humidity.",
        "pt": "Isto não cabe nos blocos PIO deste chip. Desligue um destes: Sondas de temperatura DS18B20; DHT22 temperatura e umidade."
      },
      "detail": "enc28j60 needs 17 instructions and 1 state machine and finds no block where it leaves room for the programs that load after it: pio0 has 32 instructions and 4 state machines free, but then onewire would not fit; pio1 has 28 instructions and 2 state machines free, but then dht22 would not fit.",
      "fix": [
        "sensor_ds18b20",
        "sensor_dht22"
      ],
      "lock": {
        "sensor_ds18b20": {
          "en": "This does not fit the PIO blocks of this chip. Turn off: DHT22 temperature and humidity.",
          "pt": "Isto não cabe nos blocos PIO deste chip. Desligue: DHT22 temperatura e umidade."
        },
        "sensor_dht22": {
          "en": "This does not fit the PIO blocks of this chip. Turn off: DS18B20 temperature probes.",
          "pt": "Isto não cabe nos blocos PIO deste chip. Desligue: Sondas de temperatura DS18B20."
        }
      }
    }
  ],
  "costs": {
    "_comment": [
      "Custos POR PRODUTO para o configurador (docs/configurador/). GERADO por",
      "tools/measure_savings.py --matrix; nao edite a mao. Para cada produto",
      "publicado: `base` e a imagem como o manifesto a define, e cada `flips.<chave>`",
      "e a MESMA imagem com so aquela chave invertida — used/bin/ram medidos, ou",
      "`overflow` (o excesso que o linker reporta quando nao cabe no slot), ou",
      "`rule` (a regra do manifesto que proibe a combinacao; nao foi compilada), ou",
      "`unsupported` (o driver de rede pede uma placa que o chip do produto nao tem,",
      "ou a combinacao nao cabe nos blocos de PIO ou nos pinos: nao ha imagem a",
      "medir, e a pagina a conta como sem medida).",
      "`flips.net=<id>` e a mesma coisa para a rede, que e uma escolha de driver e",
      "nao um interruptor: a MESMA imagem com o driver de rede trocado para <id>,",
      "uma entrada por driver que nao e o do produto, medida ou anotada do mesmo",
      "jeito.",
      "`same: true` diz que a imagem saiu identica a base, byte a byte: a chave nao",
      "tem consumidor naquele produto, e a pagina diz isso em vez de mostrar +0 B.",
      "A pagina SOMA as diferencas quando mais de uma chave muda: estimativa, e ela",
      "diz isso. O numero real vem da build (tools/build_custom.py). `checks` sao",
      "combinacoes compiladas de verdade: a pagina compara cada uma com a soma dela",
      "e usa o maior erro como margem de \"perto do limite\"."
    ],
    "measured_at": {
      "date": "2026-10-13",
      "src_commit": "32d0d67",
      "src_dirty": false
    },
    "products": {
      "pico_w_release": {
        "base": {
          "used": 1021132,
          "bin": 1033164,
          "ram": 111432
        },
        "flips": {
          "cli_full": {
            "overflow": 36772
          },
          "web_https": {
            "used": 996644,
            "bin": 1008676,
            "ram": 111424
          },
          "license_stub": {
            "used": 1018564,
            "bin": 1030596,
            "ram": 111432
          },
          "mdns": {
            "used": 1004548,
            "bin": 1016580,
            "ram": 111264
          },
          "concurrency_asserts": {
            "used": 1023364,
            "bin": 1035396,
            "ram": 111432
          },
          "bluetooth": {
            "overflow": 130572
          },
          "air": {
            "rule": "air-needs-headless"
          },
          "sound_buzzer": {
            "used": 1016140,
            "bin": 1028172,
            "ram": 111432
          },
          "sensor_ds18b20": {
            "used": 1015620,
            "bin": 1027652,
            "ram": 111368
          },
          "sensor_dht22": {
            "used": 1017692,
            "bin": 1029724,
            "ram": 111432
          },
          "sensor_bme280": {
            "used": 1003344,
            "bin": 1015380,
            "ram": 111212
          },
          "tel_tls": {
            "used": 1019892,
            "bin": 1031924,
            "ram": 111432
          },
          "tel_mqtt": {
            "used": 1011836,
            "bin": 1023868,
            "ram": 111432
          },
          "web_history": {
            "used": 997284,
            "bin": 1009316,
            "ram": 111432
          },
          "web_export_api": {
            "used": 1008844,
            "bin": 1020876,
            "ram": 109380
          },
          "web_metrics": {
            "used": 1016212,
            "bin": 1028244,
            "ram": 111432
          },
          "syslog": {
            "used": 1019372,
            "bin": 1031404,
            "ram": 111432
          },
          "tft_graph": {
            "used": 1000708,
            "bin": 1012740,
            "ram": 109200
          },
          "net=enc28j60": {
            "unsupported": "enc28j60 needs 17 instructions and 1 state machine and finds no block where it leaves room for the programs that load after it: pio0 has 32 instructions and 4 state machines free, but then onewire would not fit; pio1 has 28 instructions and 2 state machines free, but then dht22 would not fit."
          }
        }
      },
      "pico_w_alpha": {
        "base": {
          "used": 974972,
          "bin": 990380,
          "ram": 125680
        },
        "flips": {
          "cli_full": {
            "used": 1022788,
            "bin": 1035436,
            "ram": 123752
          },
          "web_https": {
            "used": 1000828,
            "bin": 1014956,
            "ram": 125688
          },
          "license_stub": {
            "used": 974972,
            "bin": 990380,
            "ram": 125680
          },
          "mdns": {
            "used": 991372,
            "bin": 1006764,
            "ram": 125848
          },
          "concurrency_asserts": {
            "used": 977156,
            "bin": 990380,
            "ram": 125680
          },
          "bluetooth": {
            "used": 837248,
            "bin": 849284,
            "ram": 106332
          },
          "air": {
            "rule": "air-needs-headless"
          },
          "sound_buzzer": {
            "used": 970860,
            "bin": 986284,
            "ram": 125680
          },
          "sensor_ds18b20": {
            "used": 970404,
            "bin": 986284,
            "ram": 125616
          },
          "sensor_dht22": {
            "used": 973396,
            "bin": 986284,
            "ram": 125680
          },
          "sensor_bme280": {
            "used": 960088,
            "bin": 973972,
            "ram": 125460
          },
          "tel_tls": {
            "used": 917084,
            "bin": 933036,
            "ram": 125664
          },
          "tel_mqtt": {
            "used": 967132,
            "bin": 982188,
            "ram": 125680
          },
          "web_history": {
            "used": 953204,
            "bin": 965804,
            "ram": 125680
          },
          "web_export_api": {
            "used": 963724,
            "bin": 978092,
            "ram": 123628
          },
          "web_metrics": {
            "used": 971604,
            "bin": 986284,
            "ram": 125680
          },
          "syslog": {
            "used": 973716,
            "bin": 986284,
            "ram": 125680
          },
          "tft_graph": {
            "rule": "graph-needs-tft"
          },
          "net=enc28j60": {
            "rule": "bluetooth-needs-cyw43"
          }
        }
      },
      "pico_w_air": {
        "base": {
          "used": 1018256,
          "bin": 1031244,
          "ram": 115904
        },
        "flips": {
          "cli_full": {
            "used": 970016,
            "bin": 982092,
            "ram": 117776
          },
          "web_https": {
            "overflow": 15436
          },
          "license_stub": {
            "used": 1018256,
            "bin": 1031244,
            "ram": 115904
          },
          "mdns": {
            "overflow": 3148
          },
          "concurrency_asserts": {
            "used": 1020448,
            "bin": 1035340,
            "ram": 115904
          },
          "bluetooth": {
            "used": 878404,
            "bin": 890436,
            "ram": 96588
          },
          "air": {
            "rule": "headless-needs-air"
          },
          "sound_buzzer": {
            "rule": "air-no-buzzer"
          },
          "sensor_ds18b20": {
            "used": 1013248,
            "bin": 1027148,
            "ram": 115840
          },
          "sensor_dht22": {
            "used": 1016680,
            "bin": 1031244,
            "ram": 115904
          },
          "sensor_bme280": {
            "used": 1003364,
            "bin": 1018932,
            "ram": 115684
          },
          "tel_tls": {
            "used": 960416,
            "bin": 973900,
            "ram": 115888
          },
          "tel_mqtt": {
            "used": 1010408,
            "bin": 1023052,
            "ram": 115904
          },
          "web_history": {
            "used": 992384,
            "bin": 1006668,
            "ram": 115904
          },
          "web_export_api": {
            "used": 1007360,
            "bin": 1023052,
            "ram": 113856
          },
          "web_metrics": {
            "used": 1014888,
            "bin": 1027148,
            "ram": 115904
          },
          "syslog": {
            "used": 1017008,
            "bin": 1031244,
            "ram": 115904
          },
          "tft_graph": {
            "rule": "graph-needs-tft"
          },
          "net=enc28j60": {
            "rule": "bluetooth-needs-cyw43"
          }
        }
      },
      "pico2_w_release": {
        "base": {
          "used": 997208,
          "bin": 997232,
          "ram": 125516
        },
        "flips": {
          "cli_full": {
            "used": 1044144,
            "bin": 1044168,
            "ram": 123468
          },
          "web_https": {
            "used": 974032,
            "bin": 974056,
            "ram": 125500
          },
          "license_stub": {
            "used": 994640,
            "bin": 994664,
            "ram": 125516
          },
          "mdns": {
            "used": 981272,
            "bin": 981296,
            "ram": 125348
          },
          "concurrency_asserts": {
            "used": 999296,
            "bin": 999320,
            "ram": 125516
          },
          "bluetooth": {
            "used": 1133116,
            "bin": 1136272,
            "ram": 144832
          },
          "air": {
            "rule": "air-needs-headless"
          },
          "sound_buzzer": {
            "used": 992456,
            "bin": 992480,
            "ram": 125516
          },
          "sensor_ds18b20": {
            "used": 991856,
            "bin": 991880,
            "ram": 125452
          },
          "sensor_dht22": {
            "used": 993808,
            "bin": 993832,
            "ram": 125516
          },
          "sensor_bme280": {
            "used": 979788,
            "bin": 979808,
            "ram": 125296
          },
          "tel_tls": {
            "used": 996152,
            "bin": 996176,
            "ram": 125516
          },
          "tel_mqtt": {
            "used": 988208,
            "bin": 988232,
            "ram": 125516
          },
          "web_history": {
            "used": 973328,
            "bin": 973352,
            "ram": 125516
          },
          "web_export_api": {
            "used": 985136,
            "bin": 985160,
            "ram": 123464
          },
          "web_metrics": {
            "used": 992384,
            "bin": 992408,
            "ram": 125516
          },
          "syslog": {
            "used": 995520,
            "bin": 995544,
            "ram": 125516
          },
          "tft_graph": {
            "used": 977128,
            "bin": 977152,
            "ram": 123012
          },
          "net=enc28j60": {
            "unsupported": "a rede enc28j60 nao tem placa para o chip rp2350: [net.enc28j60].board so conhece rp2040"
          }
        }
      }
    },
    "checks": [
      {
        "base": "pico_w_release",
        "set": {
          "mdns": false,
          "sound_buzzer": false,
          "sensor_bme280": false
        },
        "used": 981768,
        "bin": 993804,
        "ram": 111044
      },
      {
        "base": "pico_w_release",
        "set": {
          "web_https": false,
          "license_stub": true,
          "concurrency_asserts": true
        },
        "used": 996308,
        "bin": 1008340,
        "ram": 111424
      },
      {
        "base": "pico_w_alpha",
        "set": {
          "bluetooth": false,
          "mdns": true,
          "web_https": true,
          "cli_full": true
        },
        "used": 923280,
        "bin": 935316,
        "ram": 104612
      },
      {
        "base": "pico_w_alpha",
        "set": {
          "sound_buzzer": false,
          "sensor_dht22": false,
          "sensor_bme280": false
        },
        "used": 954328,
        "bin": 969876,
        "ram": 125460
      },
      {
        "base": "pico_w_alpha",
        "set": {
          "cli_full": true,
          "sensor_ds18b20": false
        },
        "used": 1017828,
        "bin": 1031340,
        "ram": 123688
      },
      {
        "base": "pico_w_air",
        "set": {
          "bluetooth": false,
          "web_https": true,
          "mdns": true
        },
        "used": 918148,
        "bin": 930180,
        "ram": 96764
      },
      {
        "base": "pico_w_air",
        "set": {
          "cli_full": false,
          "concurrency_asserts": true,
          "sensor_bme280": false
        },
        "used": 957316,
        "bin": 969780,
        "ram": 117556
      },
      {
        "base": "pico_w_air",
        "set": {
          "sensor_ds18b20": false,
          "sensor_dht22": false,
          "sensor_bme280": false
        },
        "used": 992116,
        "bin": 1006644,
        "ram": 115620
      },
      {
        "base": "pico_w_air",
        "set": {
          "tel_tls": false,
          "tel_mqtt": false
        },
        "used": 948592,
        "bin": 961612,
        "ram": 115888
      },
      {
        "base": "pico_w_air",
        "set": {
          "web_history": false,
          "web_export_api": false,
          "web_metrics": false,
          "syslog": false
        },
        "used": 976880,
        "bin": 990284,
        "ram": 113856
      },
      {
        "base": "pico2_w_release",
        "set": {
          "mdns": false,
          "sound_buzzer": false,
          "sensor_bme280": false
        },
        "used": 959100,
        "bin": 959120,
        "ram": 125128
      },
      {
        "base": "pico2_w_release",
        "set": {
          "bluetooth": true,
          "cli_full": true
        },
        "used": 1181892,
        "bin": 1185424,
        "ram": 142784
      }
    ]
  }
}
