Salta al contenuto principale

Sistema di Tracciabilità Alimentare: Blockchain, RFID e IoT

Nel 2013, il laboratorio dell'Autorita Irlandese per la Sicurezza Alimentare trova tracce di DNA equino in prodotti etichettati come "manzo al 100%".

Cosa ne pensi di questo articolo?

Sistema di Tracciabilità Alimentare: Blockchain, RFID e IoT
FoodTechFoto:Kampus ProductionPexels

In Breve

Nel 2013, il laboratorio dell'Autorita Irlandese per la Sicurezza Alimentare trova tracce di DNA equino in prodotti etichettati come "manzo al 100%".

Sistema di Tracciabilita Alimentare: Blockchain, RFID e IoT

Nel 2013, il laboratorio dell'Autorita Irlandese per la Sicurezza Alimentare trova tracce di DNA equino in prodotti etichettati come "manzo al 100%". Lo scandalo del cavallo si propaga in tutta Europa: 28 paesi coinvolti, oltre 900 tonnellate di carne ritirate dal mercato, costi stimati tra 10 e 14 miliardi di euro tra danni d'immagine, recall e procedimenti legali. Nel 2008, in Cina, la melamina adultera il latte in polvere destinato ai neonati: 300.000 bambini ammalati, 6 morti accertati, un trauma collettivo che distrugge la fiducia nei prodotti alimentari per un decennio.

Questi non sono incidenti isolati. Sono il sintomo strutturale di una supply chain alimentare globale che movimenta ogni anno oltre 8 trilioni di dollari di merci attraverso migliaia di intermediari, processori, logisti e rivenditori, con opacita informativa che apre le porte a frodi, adulterazioni e contaminazioni. Il costo medio di un recall alimentare negli USA supera oggi i 10 milioni di dollari solo in costi diretti (ritiro, smaltimento, procedure legali), escluso il danno reputazionale permanente. In Europa, l'EFSA gestisce mediamente 3.700 notifiche RASFF all'anno attraverso il sistema di allerta rapida per alimenti e mangimi.

La risposta tecnologica a questa crisi di fiducia ha un nome preciso: tracciabilita digitale end-to-end. Un sistema che combina RFID, QR Code, sensori IoT e blockchain per creare un registro immutabile, verificabile e consultabile di ogni evento nella vita di un prodotto alimentare: dal campo di coltivazione al carrello della spesa del consumatore finale. Il mercato globale della food traceability vale oggi 23.3 miliardi di dollari nel 2025 e crescera fino a 44.6 miliardi entro il 2034, trainato da normative sempre più stringenti e da una domanda consumer di trasparenza senza precedenti.

Questo articolo e una guida tecnica completa all'implementazione di un sistema di tracciabilita alimentare di livello industriale. Copriremo l'architettura hardware (RFID/NFC/QR), la componente IoT per il monitoraggio della cold chain, la blockchain permissioned con Hyperledger Fabric, e l'implementazione backend con Python e FastAPI. Includeremo un caso studio reale sulla filiera DOP italiana e le implicazioni normative di EU Reg. 178/2002, FSMA 204 e Farm to Fork Strategy.

Cosa Imparerai in Questo Articolo

  • Architettura end-to-end di un sistema di tracciabilita alimentare (farm to fork)
  • RFID/NFC: tipologie tag, standard GS1/EPC, costi, implementazione pratica in Python
  • QR Code e GS1 Digital Link per tracciabilita consumer-facing
  • Blockchain alimentare: Hyperledger Fabric vs Ethereum vs Polygon, pro e contro
  • Smart contract e chaincode per registrazione lotti e eventi di tracciamento
  • Backend Python/FastAPI: registrazione lotti, eventi, query genealogia
  • IoT per cold chain: BLE beacons, GPS tracking, HACCP automatizzato
  • Standard GS1 EPCIS 2.0 e normative: EU Reg. 178/2002, FSMA 204, ISO 22000
  • Case study: Parmigiano Reggiano DOP e la tracciabilita blockchain italiana
  • ROI, costi di implementazione e tabella comparativa tecnologie

Serie FoodTech - Tutti gli Articoli

#ArticoloLivelloStato
1Pipeline IoT per Agricoltura di Precisione con Python e MQTTAdvancedDisponibile
2ML Edge per Rilevamento Malattie Colture: TensorFlow Lite su Raspberry PiAdvancedDisponibile
3API Satellitari e Meteorologiche per AgriTech: Dati PredittiviAdvancedDisponibile
4Sistema di Tracciabilita Alimentare: Blockchain, RFID e IoT (sei qui)AdvancedCorrente
5Computer Vision per Controllo qualità Alimentare con PyTorch YOLOAdvancedIn arrivo
6Automazione FSMA 204: Tracking, Alert e Recall via PythonAdvancedIn arrivo
7Automazione Vertical Farming: Controllo Robotico via APIAdvancedIn arrivo
8Demand Forecasting per Riduzione Spreco: ML Time-SeriesAdvancedIn arrivo
9Dashboard Real-Time per Farm IoT con Angular e GrafanaIntermediateIn arrivo
10Supply Chain Alimentare: ETL Pattern da Farm a RetailerIntermediateIn arrivo

Il Contesto Normativo: perchè la Tracciabilita Non e Più Opzionale

La tracciabilita alimentare e passata in meno di vent'anni da buona pratica volontaria a obbligo legale vincolante in tutte le principali giurisdizioni mondiali. Capire il quadro normativo e essenziale prima di progettare qualsiasi sistema tecnico: le architetture devono essere conformi by-design, non adattate a posteriori.

EU Regulation 178/2002: Il Fondamento Europeo

Il Regolamento CE 178/2002 stabilisce i principi generali della legislazione alimentare europea e crea l'EFSA (European Food Safety Authority). L'articolo 18 e il cuore della tracciabilita europea: impone a tutti gli operatori della filiera di essere in grado di identificare qualsiasi persona che abbia fornito loro un alimento, un mangime, un animale o qualsiasi sostanza destinata a essere incorporata in un alimento. Il principio e il cosiddetto "one step back, one step forward": ogni operatore deve sapere da chi ha ricevuto e a chi ha ceduto il prodotto.

Il Regolamento non prescrive tecnologie specifiche: lascia liberta di implementazione ma impone l'efficacia. Questo significa che un sistema basato su carta e burocrazia e formalmente conforme, ma diventa impossibile da gestire quando si tratta di rispondere a un'emergenza in tempi utili. Walmart lo ha dimostrato empiricamente: prima della blockchain, tracciare un lotto di manghi richiedeva 6 giorni, 18 ore e 26 minuti. Dopo, 2,2 secondi. Quella differenza non e solo efficienza: e la differenza tra un recall mirato e un recall di massa che distrugge intere categorie produttive.

FSMA 204: Il Regime USA

Negli USA, il Food Safety Modernization Act (FSMA) del 2011 ha introdotto la Rule 204, che stabilisce requisiti dettagliati di registrazione e tracciabilita per i prodotti inclusi nella Food Traceability List (FTL): lattuga, spinaci, pomodori, uova, formaggi molli, prodotti ittici, frutta fresca tagliata e altri alimenti ad alto rischio.

Aggiornamento FSMA 204 - Marzo 2026

La compliance deadline originale del 20 gennaio 2026 e stata prorogata di 30 mesi al 20 luglio 2028 dal Continuing Appropriations Act del 2026. Tuttavia, la FDA ha dichiarato che continua a incoraggiare l'implementazione anticipata e che le aziende dovrebbero iniziare i progetti di adeguamento ora per evitare l'affollamento di richieste negli ultimi mesi. La proroga non riduce la complessità tecnica dei requisiti: Critical Tracking Events (CTE) e Key Data Elements (KDE) devono essere tracciati e trasmissibili elettronicamente in meno di 24 ore su richiesta FDA.

Farm to Fork Strategy e Digital Product Passport

La strategia europea Farm to Fork (F2F), pubblicata nel 2020 come pilastro del Green Deal, introduce l'ambizione di portare la tracciabilita digitale a livello di policy europea. Il Digital Product Passport (DPP), attualmente in fase di regolamentazione nell'ambito dell'Ecodesign for Sustainable Products Regulation (ESPR), prevede che entro il 2027 molte categorie di prodotti alimentari debbano disporre di un passaporto digitale verificabile contenente informazioni su origine, impronta ambientale e catena di custodia.

Quadro Normativo Comparativo: EU vs USA vs Resto del Mondo

GiurisdizioneNormativaScopeTecnologia RichiestaSanzioni Max
Unione EuropeaReg. 178/2002 + Reg. 2019/1381Tutti gli operatori food/feedLibera (efficace)Chiusura stabilimento
USAFSMA 204Prodotti Food Traceability ListRegistro elettronicoFino a $10M
CinaFood Safety Law 2021Importazioni + domesticoNational Traceability PlatformRitiro licenza
GiapponeFood Labeling Act 2020Prodotti freschi nazionaliVolontaria (incentivata)Moderata
IndiaFSSAI Traceability Reg. 2022Esportatori foodTRACE system obbligatorioFino a INR 10L

Architettura di Tracciabilita End-to-End: Farm to Fork

Un sistema di tracciabilita alimentare moderno non e un singolo componente tecnologico: e un'architettura stratificata che integra hardware di identificazione, reti IoT, middleware di aggregazione, blockchain come livello di trust e API consumer-facing. Ogni punto della catena genera eventi che devono essere catturati, validati, archiviati e resi verificabili.

Architettura a 6 Livelli: Farm to Fork

LivelloAttoreTecnologiaDati GeneratiStandard
1. Farm Agricoltore/Allevatore RFID UHF, sensori IoT, GPS Lotto, provenienza, input agricoli, certificazioni GS1 GLN, SGTIN
2. Processing Stabilimento di lavorazione RFID HF, barcode, vision system Trasformazione lotto, ingredienti, parametri processo, HACCP GS1 EPCIS 2.0
3. Packaging Confezionamento QR Code GS1 Digital Link, NFC Data scadenza, lotto, composizione, allergeni GS1-128, GTIN
4. Logistics Trasportatore/3PL BLE beacon, GPS tracker, data logger temp Temperatura, umidita, posizione GPS, tempi transit SSCC, GS1 EPCIS
5. Retail Grande Distribuzione RFID UHF shelf, POS scanner Ricezione merce, vendita, data consumo, scarti GS1 EDI, EPCIS
6. Consumer Consumatore finale Smartphone NFC/QR scan Verifica autenticita, storia prodotto, certificazioni GS1 Digital Link URI

Il layer orizzontale che attraversa tutti i livelli e la blockchain permissioned: ogni evento critico (CTE - Critical Tracking Event) viene scritto on-chain da tutti gli attori, creando un registro immutabile della vita del prodotto. I dati di dettaglio (immagini, documenti, misurazioni continue) rimangono off-chain nei sistemi degli operatori, ma il loro hash crittografico viene ancorato on-chain per garantirne l'integrita.

RFID e NFC per Food Tracking: Guida Tecnica Completa

RFID (Radio Frequency Identification) e la tecnologia backbone della tracciabilita fisica nella supply chain alimentare. A differenza del barcode tradizionale, RFID non richiede line-of-sight, permette la lettura simultanea di centinaia di tag e può essere letto attraverso imballaggi (eccezione: materiali metallici e liquidi ad alto contenuto d'acqua, che degradano il segnale UHF fino all'80%).

Tipologie di Tag RFID per il Food Industry

Confronto Tecnologie di Identificazione per Food Traceability

TecnologiaFrequenzaRange LetturaCosto TagUso TipicoResistenza
RFID UHF (Gen2) 860-960 MHz 1-10 m 0.05-0.30 EUR Pallet, imballi, cartoni Standard (no metallo/acqua)
RFID HF (ISO 15693) 13.56 MHz 10-100 cm 0.30-1.50 EUR Singolo prodotto, farmaci, DOP Buona (liquidi tollerati)
NFC (ISO 14443) 13.56 MHz 0-10 cm 0.20-2.00 EUR Consumer-facing, DOP anticontraffazione Buona (smartphone compatibile)
BLE Beacon 2.4 GHz 1-50 m 5-30 EUR Cold chain, pallet con temperatura Ottima (batteria integrata)
QR Code GS1 N/A (ottico) 5 cm - 5 m 0.001 EUR (stampa) Packaging consumer, DL URI Dipende da stampa
p-Chip (micro-transponder) UHF specializzato 0-5 cm 0.10-0.50 EUR Formaggi DOP, prodotti premium Eccellente (inossidabile)

Standard GS1 per RFID: SGTIN e EPC

Lo standard globale per la codifica degli identificatori RFID in ambito food e il GS1 EPC (Electronic Product Code). Il codice SGTIN-96 (Serialized Global Trade Item Number) permette di identificare univocamente ogni singola unita di prodotto a livello globale:

# Struttura SGTIN-96 (96 bit)
# {Header}.{Filter}.{Partition}.{CompanyPrefix}.{ItemReference}.{SerialNumber}

# Esempio SGTIN per Parmigiano Reggiano DOP
# EPC URN format:
urn:epc:id:sgtin:8012345.067890.123456789

# Decodifica:
# 8012345    = GS1 Company Prefix (assegnato a caseificio specifico)
# 067890     = Item Reference (codice prodotto specifico - forma DOP 24 mesi)
# 123456789  = Serial Number (numero forma univoco)

# Conversione a GTIN-14:
# 0 8012345 067890 [check digit]
# GTIN-14: 08012345067890X

# Python: lettura e parsing SGTIN
import re
from dataclasses import dataclass

@dataclass
class SGTIN:
    company_prefix: str
    item_reference: str
    serial_number: str
    gtin: str = ""

    def to_urn(self) -> str:
        return f"urn:epc:id:sgtin:{self.company_prefix}.{self.item_reference}.{self.serial_number}"

    def to_gtin14(self) -> str:
        """Calcola GTIN-14 da company prefix + item reference"""
        raw = self.company_prefix + self.item_reference
        # Calcola check digit GS1 (Luhn-like algorithm)
        total = 0
        for i, digit in enumerate(reversed(raw)):
            n = int(digit)
            if i % 2 == 0:
                n *= 3
            total += n
        check = (10 - (total % 10)) % 10
        return f"0{raw}{check}"


def parse_epc_urn(urn: str) -> SGTIN:
    """Parsa un EPC URN SGTIN e restituisce oggetto strutturato"""
    pattern = r"urn:epc:id:sgtin:(\d+)\.(\d+)\.(\d+)"
    match = re.match(pattern, urn)
    if not match:
        raise ValueError(f"URN non valido: {urn}")

    sgtin = SGTIN(
        company_prefix=match.group(1),
        item_reference=match.group(2),
        serial_number=match.group(3)
    )
    sgtin.gtin = sgtin.to_gtin14()
    return sgtin


# Utilizzo
epc = parse_epc_urn("urn:epc:id:sgtin:8012345.067890.123456789")
print(f"Company: {epc.company_prefix}")   # 8012345
print(f"GTIN-14: {epc.gtin}")             # 080123450678906
print(f"URN: {epc.to_urn()}")

Implementazione Lettore RFID con Python

Il seguente esempio mostra l'integrazione con un lettore RFID UHF industriale (compatibile con Zebra FX9600 o Impinj Speedway) tramite LLRP (Low Level Reader Protocol) o interfaccia REST proprietaria:

import asyncio
import json
from datetime import datetime, timezone
from dataclasses import dataclass, field, asdict
from typing import Optional
import httpx

@dataclass
class RFIDEvent:
    """Evento RFID catturato da lettore industriale"""
    epc: str                      # Electronic Product Code
    antenna_id: int               # ID antenna che ha letto il tag
    rssi: float                   # Signal strength in dBm
    timestamp: str                # ISO 8601 UTC
    reader_id: str                # ID lettore (es. "DOCK-01")
    location: str                 # Posizione fisica (es. "INGRESSO-MAGAZZINO")
    direction: Optional[str] = None  # "IN" | "OUT" | None

    @classmethod
    def from_reader_data(cls, raw: dict, reader_id: str, location: str) -> "RFIDEvent":
        return cls(
            epc=raw["epc"],
            antenna_id=raw.get("antenna", 1),
            rssi=raw.get("rssi", -70.0),
            timestamp=datetime.now(timezone.utc).isoformat(),
            reader_id=reader_id,
            location=location
        )


class RFIDEventProcessor:
    """
    Processore eventi RFID con deduplicazione e buffer
    Gestisce reader industriali via REST API (compatibile Zebra, Impinj)
    """

    def __init__(self, reader_url: str, reader_id: str, location: str):
        self.reader_url = reader_url
        self.reader_id = reader_id
        self.location = location
        self._seen_epcs: dict[str, float] = {}     # EPC -> timestamp last seen
        self.dedup_window_seconds = 2.0           # Anti-bounce window

    def _is_duplicate(self, epc: str, now: float) -> bool:
        """Filtra letture duplicate nel finestra temporale"""
        last_seen = self._seen_epcs.get(epc)
        if last_seen and (now - last_seen) < self.dedup_window_seconds:
            return True
        self._seen_epcs[epc] = now
        return False

    async def poll_reader(self) -> list[RFIDEvent]:
        """Interroga il lettore RFID e restituisce eventi unici"""
        async with httpx.AsyncClient(timeout=5.0) as client:
            resp = await client.get(f"{self.reader_url}/api/v1/tags")
            resp.raise_for_status()
            tags_raw = resp.json().get("tags", [])

        now = datetime.now(timezone.utc).timestamp()
        events = []
        for raw in tags_raw:
            epc = raw.get("epc", "")
            if not epc or self._is_duplicate(epc, now):
                continue
            event = RFIDEvent.from_reader_data(raw, self.reader_id, self.location)
            events.append(event)

        return events

    async def stream_events(self, callback, poll_interval: float = 0.5):
        """Stream continuo di eventi RFID con callback"""
        print(f"Avvio polling RFID reader {self.reader_id} @ {self.reader_url}")
        while True:
            try:
                events = await self.poll_reader()
                for event in events:
                    await callback(event)
            except Exception as e:
                print(f"Errore reader {self.reader_id}: {e}")
            await asyncio.sleep(poll_interval)


# Utilizzo
async def handle_rfid_event(event: RFIDEvent):
    print(f"Lotto {event.epc} rilevato in {event.location} @ {event.timestamp}")
    # Invia a pipeline tracciabilita
    await send_to_traceability_pipeline(event)

async def send_to_traceability_pipeline(event: RFIDEvent):
    async with httpx.AsyncClient() as client:
        await client.post(
            "http://traceability-api:8000/api/v1/events",
            json=asdict(event)
        )

QR Code e GS1 Digital Link: Tracciabilita Consumer-Facing

Mentre RFID e invisibile al consumatore finale, il QR Code rappresenta il punto di contatto diretto tra il sistema di tracciabilita e chi acquista il prodotto. La semplice lettura con uno smartphone Apple o Android diventa una finestra sulla storia completa del prodotto.

GS1 ha sviluppato lo standard GS1 Digital Link URI per codificare nei QR Code non solo il GTIN del prodotto, ma anche lotto, data di scadenza e link diretto alle informazioni di tracciabilita online. L'URL GS1 Digital Link ha questa struttura standard:

# GS1 Digital Link URI format
# https://{domain}/{primary-key-qualifier}/{value}/{data-qualifier}/{value}?{params}

# Esempio per Parmigiano Reggiano DOP:
# https://trace.parmigianoreggiano.it/01/08012345678905/10/LOT2025001A/17/261231

# Decodifica:
# /01/ = GTIN (Application Identifier AI 01)
# 08012345678905 = GTIN-14 della forma di Parmigiano
# /10/ = Batch/Lot Number (AI 10)
# LOT2025001A = numero lotto specifico
# /17/ = Expiration Date (AI 17)
# 261231 = 31 dicembre 2026

# Python: generazione GS1 Digital Link QR Code
import qrcode
from urllib.parse import urlencode

def generate_gs1_digital_link(
    gtin: str,          # 14 digits
    lot: str,           # batch number
    expiry: str,        # YYMMDD format
    domain: str,
    serial: str = None
) -> str:
    """
    Genera URI GS1 Digital Link compliant (GS1 General Specifications 24.0)
    """
    # Validazione GTIN-14
    if len(gtin) != 14 or not gtin.isdigit():
        raise ValueError(f"GTIN deve essere 14 cifre, ricevuto: {gtin}")

    # Build URI path
    uri = f"https://{domain}/01/{gtin}/10/{lot}/17/{expiry}"

    if serial:
        uri += f"/21/{serial}"

    return uri


def generate_gs1_qr_code(digital_link_uri: str, output_path: str) -> None:
    """
    Genera QR Code GS1 Digital Link con specifiche standard
    Versione QR: automatica, ECC Level M (minimo GS1)
    """
    qr = qrcode.QRCode(
        version=None,           # auto-size
        error_correction=qrcode.constants.ERROR_CORRECT_M,
        box_size=10,
        border=4,               # quiet zone: min 4 moduli standard GS1
    )
    qr.add_data(digital_link_uri)
    qr.make(fit=True)

    img = qr.make_image(fill_color="black", back_color="white")
    img.save(output_path)
    print(f"QR Code salvato in: {output_path}")


# Esempio utilizzo
gtin_parmigiano = "08012345678905"
lot_number = "PR2025A001"
expiry_date = "261231"   # 31 dicembre 2026

uri = generate_gs1_digital_link(
    gtin=gtin_parmigiano,
    lot=lot_number,
    expiry=expiry_date,
    domain="trace.parmigianoreggiano.it",
    serial="FRM042025001"  # numero forma specifico
)
print(f"Digital Link URI: {uri}")
# Output: https://trace.parmigianoreggiano.it/01/08012345678905/10/PR2025A001/17/261231/21/FRM042025001

generate_gs1_qr_code(uri, "/output/parmigiano_qr.png")

Blockchain per Tracciabilita Alimentare: Architettura e Implementazione

La blockchain e la componente che porta la tracciabilita da sistema di registrazione a sistema di trust. Senza blockchain (o un equivalente tecnologico di registro distribuito), i dati di tracciabilita risiedono in sistemi centralizzati controllati da un singolo attore: il produttore, il retailer o il software vendor. Questo significa che i dati possono essere modificati, cancellati o semplicemente non condivisi.

La blockchain introduce due proprietà fondamentali: immutabilita (un dato scritto on-chain non può essere alterato retroattivamente senza invalidare l'intera catena) e decentralizzazione del trust (nessun singolo attore controlla il registro: tutti i partecipanti ne hanno una copia verificata).

Hyperledger Fabric vs Ethereum vs Polygon: Quale Scegliere?

Confronto Blockchain per Food Traceability Enterprise

CaratteristicaHyperledger FabricEthereum (Public)Polygon PoSVeChain
Tipo Permissioned privata Public permissionless Public L2 (EVM) Enterprise permissioned
TPS (throughput) 3.000-20.000 TPS 15-30 TPS 7.000+ TPS 10.000 TPS
Costo transazione Infrastruttura (nessun gas) $1-50+ (volatile) $0.001-0.01 $0.0001 (VTHO)
Privacy dati Alta (channels privati) Nessuna (public) Limitata Alta (private channels)
Governance Consorzio (MSP) Decentralizzata Decentralizzata VeChain Foundation
Identita partecipanti Certificati X.509 (MSP) Indirizzi pseudonimi Indirizzi pseudonimi Identity verificata
Case study food IBM Food Trust, Walmart Niche/startup Emergente Walmart China, Bright Food
Linguaggio contratti Go, Node.js, Java (Chaincode) Solidity Solidity Solidity (EVM compatibile)
Setup complessità Alta (orderer, MSP, channels) Bassa Bassa Media
Raccomandato per Consorzi enterprise, DOP, retail Token, NFT prodotto Startup, B2C transparency Supply chain Asia, farmaceutici

Per la maggior parte dei progetti di tracciabilita alimentare enterprise, la scelta e Hyperledger Fabric: permette di costruire un consorzio in cui ogni operatore della filiera (produttore, processore, logista, retailer) diventa un "peer" con identità certificata, controllando esattamente quali dati condivide e con chi, attraverso il meccanismo dei channels e delle private data collections.

Chaincode Hyperledger Fabric per Food Traceability

Il chaincode e il "smart contract" di Hyperledger Fabric. Il seguente esempio in Go implementa le funzioni core per la tracciabilita di un lotto alimentare:

// Chaincode Go per Hyperledger Fabric 2.x
// File: food_trace_chaincode.go

package main

import (
    "encoding/json"
    "fmt"
    "time"

    "github.com/hyperledger/fabric-contract-api-go/contractapi"
)

// FoodLot rappresenta un lotto alimentare on-chain
type FoodLot struct{
    LotID          string    `json:"lot_id"`
    ProductGTIN    string    `json:"product_gtin"`
    ProducerGLN    string    `json:"producer_gln"`
    ProductionDate string    `json:"production_date"`
    ExpiryDate     string    `json:"expiry_date"`
    CertificateIDs []string  `json:"certificate_ids"`
    LotStatus      string    `json:"lot_status"`       // ACTIVE | RECALLED | EXPIRED
    CreatedAt      string    `json:"created_at"`
    UpdatedAt      string    `json:"updated_at"`
}

// TraceEvent rappresenta un evento di tracciamento sulla supply chain
type TraceEvent struct{
    EventID      string    `json:"event_id"`
    LotID        string    `json:"lot_id"`
    EventType    string    `json:"event_type"`    // CREATED | SHIPPED | RECEIVED | TRANSFORMED | SOLD
    ActorGLN     string    `json:"actor_gln"`     // GS1 Global Location Number dell'attore
    Location     string    `json:"location"`
    Timestamp    string    `json:"timestamp"`
    Temperature  *float64  `json:"temperature,omitempty"`
    Humidity     *float64  `json:"humidity,omitempty"`
    Notes        string    `json:"notes,omitempty"`
    DataHash     string    `json:"data_hash"`     // SHA-256 di dati off-chain
}

// FoodTraceContract e il contratto principale
type FoodTraceContract struct{
    contractapi.Contract
}

// RegisterLot registra un nuovo lotto alimentare
func (c *FoodTraceContract) RegisterLot(
    ctx contractapi.TransactionContextInterface,
    lotID, productGTIN, producerGLN, productionDate, expiryDate string,
    certificateIDs []string,
) error {
    // Verifica che il lotto non esista gia
    existing, err := ctx.GetStub().GetState(lotID)
    if err != nil {
        return fmt.Errorf("errore accesso ledger: %v", err)
    }
    if existing != nil {
        return fmt.Errorf("lotto %s già registrato", lotID)
    }

    now := time.Now().UTC().Format(time.RFC3339)
    lot := FoodLot{
        LotID:          lotID,
        ProductGTIN:    productGTIN,
        ProducerGLN:    producerGLN,
        ProductionDate: productionDate,
        ExpiryDate:     expiryDate,
        CertificateIDs: certificateIDs,
        LotStatus:      "ACTIVE",
        CreatedAt:      now,
        UpdatedAt:      now,
    }

    lotJSON, err := json.Marshal(lot)
    if err != nil {
        return err
    }

    return ctx.GetStub().PutState(lotID, lotJSON)
}

// AddTraceEvent aggiunge un evento di tracciamento al lotto
func (c *FoodTraceContract) AddTraceEvent(
    ctx contractapi.TransactionContextInterface,
    eventJSON string,
) error {
    var event TraceEvent
    if err := json.Unmarshal([]byte(eventJSON), &event); err != nil {
        return fmt.Errorf("JSON evento non valido: %v", err)
    }

    // Verifica che il lotto esista e sia attivo
    lotBytes, err := ctx.GetStub().GetState(event.LotID)
    if err != nil || lotBytes == nil {
        return fmt.Errorf("lotto %s non trovato", event.LotID)
    }

    var lot FoodLot
    json.Unmarshal(lotBytes, &lot)
    if lot.LotStatus != "ACTIVE" {
        return fmt.Errorf("lotto non attivo (status: %s)", lot.LotStatus)
    }

    event.Timestamp = time.Now().UTC().Format(time.RFC3339)

    // Chiave composita per eventi: "EVENT" + lotID + eventID
    eventKey, err := ctx.GetStub().CreateCompositeKey("EVENT", []string{event.LotID, event.EventID})
    if err != nil {
        return err
    }

    eventJSON, err := json.Marshal(event)
    if err != nil {
        return err
    }

    return ctx.GetStub().PutState(eventKey, eventJSON)
}

// GetLotHistory restituisce la storia completa di un lotto
func (c *FoodTraceContract) GetLotHistory(
    ctx contractapi.TransactionContextInterface,
    lotID string,
) ([]TraceEvent, error) {
    iterator, err := ctx.GetStub().GetStateByPartialCompositeKey("EVENT", []string{lotID})
    if err != nil {
        return nil, err
    }
    defer iterator.Close()

    var events []TraceEvent
    for iterator.HasNext() {
        result, err := iterator.Next()
        if err != nil {
            continue
        }
        var event TraceEvent
        if err := json.Unmarshal(result.Value, &event); err == nil {
            events = append(events, event)
        }
    }
    return events, nil
}

// RecallLot avvia un recall su un lotto
func (c *FoodTraceContract) RecallLot(
    ctx contractapi.TransactionContextInterface,
    lotID, reason string,
) error {
    lotBytes, err := ctx.GetStub().GetState(lotID)
    if err != nil || lotBytes == nil {
        return fmt.Errorf("lotto %s non trovato", lotID)
    }

    var lot FoodLot
    json.Unmarshal(lotBytes, &lot)
    lot.LotStatus = "RECALLED"
    lot.UpdatedAt = time.Now().UTC().Format(time.RFC3339)

    lotJSON, _ := json.Marshal(lot)
    return ctx.GetStub().PutState(lotID, lotJSON)
}

Backend Python/FastAPI: API di Tracciabilita

Il backend applicativo fa da middleware tra i sistemi di campo (lettori RFID, IoT) e la blockchain. Espone REST API standardizzate per registrazione lotti, aggiunta eventi e query della genealogia di un prodotto. I dati di dettaglio vengono archiviati in PostgreSQL (con pgvector per ricerca semantica futura), mentre i loro hash vengono scritti on-chain tramite Hyperledger Fabric SDK.

# food_traceability_api.py
# FastAPI backend per sistema di tracciabilita alimentare
# Integra PostgreSQL (dati) + Hyperledger Fabric (trust layer)

from fastapi import FastAPI, HTTPException, Depends, status
from pydantic import BaseModel, Field, field_validator
from datetime import datetime, timezone, date
from typing import Optional, List
import hashlib
import json
import uuid
import asyncpg

app = FastAPI(
    title="Food Traceability API",
    description="Sistema di tracciabilita alimentare end-to-end",
    version="2.0.0"
)

# ---- Modelli Pydantic ----

class LotRegistration(BaseModel):
    """Schema per registrazione nuovo lotto alimentare"""
    lot_id: str = Field(..., min_length=3, max_length=50, pattern=r"^[A-Z0-9\-]+$")
    product_gtin: str = Field(..., min_length=14, max_length=14)
    producer_gln: str = Field(..., description="GS1 Global Location Number del produttore")
    product_name: str = Field(..., max_length=200)
    production_date: date
    expiry_date: date
    quantity_kg: float = Field(..., gt=0)
    origin_country: str = Field(..., max_length=2)  # ISO 3166-1 alpha-2
    certifications: List[str] = Field(default_factory=list)
    raw_materials: List[dict] = Field(default_factory=list)

    @field_validator("product_gtin")
    @classmethod
    def validate_gtin14(cls, v: str) -> str:
        """Valida GTIN-14 con GS1 check digit algorithm"""
        if not v.isdigit():
            raise ValueError("GTIN deve contenere solo cifre")
        digits = [int(d) for d in v]
        total = sum(
            d * (3 if (len(digits) - 1 - i) % 2 == 0 else 1)
            for i, d in enumerate(digits[:-1])
        )
        expected_check = (10 - (total % 10)) % 10
        if digits[-1] != expected_check:
            raise ValueError(f"Check digit GTIN non valido. Atteso: {expected_check}")
        return v

    @field_validator("expiry_date")
    @classmethod
    def expiry_after_production(cls, v, info):
        if "production_date" in info.data and v <= info.data["production_date"]:
            raise ValueError("Data scadenza deve essere successiva alla data produzione")
        return v


class TraceEventRequest(BaseModel):
    """Schema per aggiunta evento di tracciamento"""
    lot_id: str
    event_type: str = Field(..., pattern=r"^(CREATED|SHIPPED|RECEIVED|TRANSFORMED|STORED|SOLD|RECALLED)$")
    actor_gln: str
    actor_name: str
    location_name: str
    location_gln: Optional[str] = None
    latitude: Optional[float] = None
    longitude: Optional[float] = None
    temperature_celsius: Optional[float] = None
    humidity_percent: Optional[float] = None
    quantity_kg: Optional[float] = None
    notes: Optional[str] = None
    additional_data: dict = Field(default_factory=dict)


class LotGenealogy(BaseModel):
    """Genealogia completa di un lotto"""
    lot_id: str
    product_name: str
    product_gtin: str
    producer_name: str
    production_date: str
    expiry_date: str
    status: str
    certifications: List[str]
    events: List[dict]
    blockchain_tx_hash: Optional[str] = None
    raw_materials: List[dict]


# ---- Endpoints ----

@app.post("/api/v1/lots", status_code=status.HTTP_201_CREATED)
async def register_lot(lot: LotRegistration, db=Depends(get_db)):
    """
    Registra un nuovo lotto alimentare.
    Scrive il record in PostgreSQL e l'hash on-chain su Hyperledger Fabric.
    """
    # Verifica unicita lotto
    existing = await db.fetchrow(
        "SELECT lot_id FROM food_lots WHERE lot_id = $1", lot.lot_id
    )
    if existing:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail=f"Lotto {lot.lot_id} già registrato"
        )

    # Calcola hash dei dati per ancoraggio blockchain
    lot_data = lot.model_dump(mode="json")
    lot_json_str = json.dumps(lot_data, sort_keys=True, default=str)
    data_hash = hashlib.sha256(lot_json_str.encode()).hexdigest()

    # Persisti su PostgreSQL
    await db.execute("""
        INSERT INTO food_lots (
            lot_id, product_gtin, producer_gln, product_name,
            production_date, expiry_date, quantity_kg, origin_country,
            certifications, raw_materials, lot_status, data_hash, created_at
        ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::jsonb, $10::jsonb, 'ACTIVE', $11, NOW())
    """,
        lot.lot_id, lot.product_gtin, lot.producer_gln, lot.product_name,
        lot.production_date, lot.expiry_date, lot.quantity_kg, lot.origin_country,
        json.dumps(lot.certifications), json.dumps(lot.raw_materials), data_hash
    )

    # Scrivi hash su blockchain (asincrono)
    tx_hash = await write_to_blockchain(lot.lot_id, data_hash, "REGISTER_LOT")

    return {
        "lot_id": lot.lot_id,
        "status": "registered",
        "data_hash": data_hash,
        "blockchain_tx": tx_hash,
        "message": f"Lotto {lot.lot_id} registrato con successo"
    }


@app.post("/api/v1/lots/{lot_id}/events", status_code=status.HTTP_201_CREATED)
async def add_trace_event(lot_id: str, event: TraceEventRequest, db=Depends(get_db)):
    """Aggiunge un evento di tracciamento a un lotto esistente"""
    # Verifica lotto esiste ed e attivo
    lot = await db.fetchrow(
        "SELECT lot_id, lot_status FROM food_lots WHERE lot_id = $1", lot_id
    )
    if not lot:
        raise HTTPException(status_code=404, detail=f"Lotto {lot_id} non trovato")
    if lot["lot_status"] not in ("ACTIVE", "STORED"):
        raise HTTPException(
            status_code=400,
            detail=f"Lotto in stato {lot['lot_status']}: non accetta nuovi eventi"
        )

    event_id = str(uuid.uuid4())
    timestamp = datetime.now(timezone.utc).isoformat()

    # Hash dati evento per blockchain
    event_data = event.model_dump(mode="json")
    event_data["event_id"] = event_id
    event_data["timestamp"] = timestamp
    event_json = json.dumps(event_data, sort_keys=True, default=str)
    event_hash = hashlib.sha256(event_json.encode()).hexdigest()

    # Persisti evento
    await db.execute("""
        INSERT INTO trace_events (
            event_id, lot_id, event_type, actor_gln, actor_name,
            location_name, location_gln, latitude, longitude,
            temperature_celsius, humidity_percent, quantity_kg,
            notes, additional_data, data_hash, created_at
        ) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14::jsonb,$15,NOW())
    """,
        event_id, lot_id, event.event_type, event.actor_gln, event.actor_name,
        event.location_name, event.location_gln, event.latitude, event.longitude,
        event.temperature_celsius, event.humidity_percent, event.quantity_kg,
        event.notes, json.dumps(event.additional_data), event_hash
    )

    # Aggiorna status lotto
    status_map = {"SHIPPED": "IN_TRANSIT", "RECEIVED": "STORED", "SOLD": "SOLD"}
    if event.event_type in status_map:
        await db.execute(
            "UPDATE food_lots SET lot_status = $1 WHERE lot_id = $2",
            status_map[event.event_type], lot_id
        )

    tx_hash = await write_to_blockchain(lot_id, event_hash, event.event_type)

    return {"event_id": event_id, "blockchain_tx": tx_hash, "status": "recorded"}


@app.get("/api/v1/lots/{lot_id}/genealogy", response_model=LotGenealogy)
async def get_lot_genealogy(lot_id: str, db=Depends(get_db)):
    """Restituisce la genealogia completa di un lotto: dati base + tutti gli eventi"""
    lot = await db.fetchrow("""
        SELECT l.*, p.name as producer_name
        FROM food_lots l
        LEFT JOIN producers p ON l.producer_gln = p.gln
        WHERE l.lot_id = $1
    """, lot_id)

    if not lot:
        raise HTTPException(status_code=404, detail=f"Lotto {lot_id} non trovato")

    events = await db.fetch("""
        SELECT * FROM trace_events
        WHERE lot_id = $1
        ORDER BY created_at ASC
    """, lot_id)

    blockchain_tx = await get_blockchain_anchor(lot_id)

    return LotGenealogy(
        lot_id=lot["lot_id"],
        product_name=lot["product_name"],
        product_gtin=lot["product_gtin"],
        producer_name=lot.get("producer_name", lot["producer_gln"]),
        production_date=str(lot["production_date"]),
        expiry_date=str(lot["expiry_date"]),
        status=lot["lot_status"],
        certifications=json.loads(lot["certifications"] or "[]"),
        events=[dict(e) for e in events],
        blockchain_tx_hash=blockchain_tx,
        raw_materials=json.loads(lot["raw_materials"] or "[]")
    )


async def write_to_blockchain(lot_id: str, data_hash: str, event_type: str) -> str:
    """
    Scrive un hash su Hyperledger Fabric via REST API (Fabric Gateway API)
    In produzione: usa fabric-sdk-py o Fabric Gateway REST proxy
    """
    # Stub: in produzione integra con fabric-gateway
    tx_id = hashlib.sha256(f"{lot_id}{data_hash}{event_type}".encode()).hexdigest()
    return f"0x{tx_id}"


async def get_db():
    """Dependency: connessione PostgreSQL via asyncpg"""
    conn = await asyncpg.connect("postgresql://user:pass@localhost/traceability")
    try:
        yield conn
    finally:
        await conn.close()

IoT per Cold Chain: Monitoraggio Temperatura e HACCP Automatizzato

La cold chain (catena del freddo) e il punto critico della sicurezza alimentare per latticini, carni, pesce e prodotti freschi. Il 20-30% delle rotture della cold chain avviene durante il trasporto, e ogni deviazione di temperatura fuori range può determinare proliferazione batterica esponenziale e rischi sanitari. Un sistema IoT integrato permette il monitoraggio continuo con alert automatici e registrazione conforme ai requisiti HACCP (Hazard Analysis and Critical Control Points).

Architettura IoT Cold Chain

Stack Tecnologico IoT per Cold Chain Monitoring

ComponenteTecnologiaProtocolloCosto Indicativo
Sensore temperatura/umidita Ruuvi Tag, Minew S1, Onset HOBO BLE 4.2/5.0 15-80 EUR/unita
Gateway BLE-to-Cloud Raspberry Pi 4 + BLE dongle MQTT su 4G/WiFi 80-200 EUR
GPS tracker pallet Teltonika FMB920, Queclink GL320MG LTE-M, MQTT 60-150 EUR
Data logger standalone Tec4med D2, Berlinger FRIDGE-tag USB/NFC download 30-120 EUR
Time-series database InfluxDB 3.x, TimescaleDB HTTP/Line Protocol Open source / Cloud
Alert e notifiche Grafana Alerting, PagerDuty Webhook, Email, SMS Dipende da volume
# cold_chain_monitor.py
# Monitor IoT per cold chain con BLE scanning e MQTT publishing
# Compatible con Ruuvi Tag v2 (formato RAWv2)

import asyncio
import json
import struct
from datetime import datetime, timezone
from dataclasses import dataclass, asdict
from typing import Optional
import paho.mqtt.client as mqtt

# Configurazione limiti HACCP per categoria prodotto
HACCP_LIMITS = {
    "dairy":       {"min": 0.0, "max": 4.0,  "alert_threshold": 6.0},
    "fresh_meat":  {"min": -2.0, "max": 4.0, "alert_threshold": 5.0},
    "frozen":      {"min": -25.0, "max": -18.0, "alert_threshold": -15.0},
    "fish":        {"min": -1.0, "max": 2.0,  "alert_threshold": 4.0},
    "vegetables":  {"min": 2.0, "max": 8.0,   "alert_threshold": 10.0},
}

@dataclass
class ColdChainReading:
    """Lettura sensore cold chain"""
    sensor_mac: str
    lot_id: str
    timestamp: str
    temperature_c: float
    humidity_pct: float
    battery_pct: int
    latitude: Optional[float] = None
    longitude: Optional[float] = None
    product_category: str = "dairy"
    alert: bool = False
    alert_reason: str = ""

    def check_haccp_compliance(self) -> None:
        """Verifica compliance HACCP e setta flag alert"""
        limits = HACCP_LIMITS.get(self.product_category, HACCP_LIMITS["dairy"])
        if self.temperature_c > limits["alert_threshold"]:
            self.alert = True
            self.alert_reason = (
                f"TEMPERATURA CRITICA: {self.temperature_c:.1f}C "
                f"(limite: {limits['alert_threshold']}C)"
            )
        elif self.temperature_c < limits["min"]:
            self.alert = True
            self.alert_reason = (
                f"TEMPERATURA SOTTO MINIMO: {self.temperature_c:.1f}C "
                f"(minimo: {limits['min']}C)"
            )
        elif self.temperature_c > limits["max"]:
            # Fuori range ma non ancora critico
            self.alert_reason = (
                f"Temperatura fuori range HACCP: {self.temperature_c:.1f}C "
                f"(range: {limits['min']}-{limits['max']}C)"
            )


def parse_ruuvi_rawv2(manufacturer_data: bytes) -> dict:
    """
    Parsa il payload RAWv2 di Ruuvi Tag (formato 0x05)
    Documentazione: https://docs.ruuvi.com/communication/bluetooth-advertisements/data-format-5-rawv2
    """
    if len(manufacturer_data) < 24 or manufacturer_data[0] != 0x05:
        raise ValueError("Formato Ruuvi RAWv2 non valido")

    # Unpack: temperatura (int16, 0.005 C/unit), umidita (uint16, 0.0025 %/unit)
    temp_raw = struct.unpack_from(">h", manufacturer_data, 1)[0]
    hum_raw = struct.unpack_from(">H", manufacturer_data, 3)[0]
    batt_raw = struct.unpack_from(">H", manufacturer_data, 12)[0]

    temperature = temp_raw * 0.005
    humidity = hum_raw * 0.0025
    battery_mv = ((batt_raw >> 5) + 1600)  # mV
    battery_pct = max(0, min(100, int((battery_mv - 2200) / 12)))

    return {
        "temperature_c": round(temperature, 2),
        "humidity_pct": round(humidity, 1),
        "battery_pct": battery_pct,
    }


class ColdChainMQTTPublisher:
    """Pubblica letture cold chain su MQTT broker"""

    def __init__(self, broker_host: str, broker_port: int = 1883):
        self.client = mqtt.Client(client_id="cold-chain-monitor-01")
        self.client.connect(broker_host, broker_port, keepalive=60)
        self.client.loop_start()

    def publish_reading(self, reading: ColdChainReading) -> None:
        topic = f"coldchain/{reading.lot_id}/sensors/{reading.sensor_mac}"
        payload = json.dumps(asdict(reading), default=str)
        self.client.publish(topic, payload, qos=1, retain=False)

        if reading.alert:
            alert_topic = f"coldchain/alerts/{reading.lot_id}"
            self.client.publish(alert_topic, payload, qos=2)
            print(f"ALERT {reading.lot_id}: {reading.alert_reason}")


async def cold_chain_pipeline(publisher: ColdChainMQTTPublisher, lot_id: str):
    """
    Pipeline simulata - in produzione: integra con BLE scanner (bleak library)
    e gateway che forwarda dati da sensori fisici
    """
    import random
    import time

    sensor_mac = "AA:BB:CC:DD:EE:FF"
    product_category = "dairy"

    while True:
        # Simulazione lettura sensore (sostituire con bleak BLE scan)
        simulated_temp = round(random.gauss(3.5, 1.2), 2)  # Media 3.5C, std 1.2
        simulated_hum = round(random.gauss(85.0, 5.0), 1)

        reading = ColdChainReading(
            sensor_mac=sensor_mac,
            lot_id=lot_id,
            timestamp=datetime.now(timezone.utc).isoformat(),
            temperature_c=simulated_temp,
            humidity_pct=simulated_hum,
            battery_pct=85,
            product_category=product_category
        )
        reading.check_haccp_compliance()
        publisher.publish_reading(reading)

        print(f"{reading.timestamp} | Temp: {reading.temperature_c}C | "
              f"Hum: {reading.humidity_pct}% | Alert: {reading.alert}")

        await asyncio.sleep(30)  # Lettura ogni 30 secondi

# Avvio pipeline
# publisher = ColdChainMQTTPublisher("mqtt.traceability.local")
# asyncio.run(cold_chain_pipeline(publisher, "LOT-PR2025-001"))

Standard GS1 EPCIS 2.0: Il Linguaggio Comune della Tracciabilita

EPCIS (Electronic Product Code Information Services) e lo standard GS1 che definisce come gli eventi di tracciabilita devono essere strutturati e condivisi tra sistemi eterogenei. La versione 2.0, pubblicata nel 2022 e aggiornata con guideline FSMA 204 nel maggio 2025, rappresenta una riscrittura radicale che porta la tracciabilita supply chain nell'era moderna del web con supporto nativo JSON/JSON-LD e REST API.

I 5 Tipi di Evento EPCIS 2.0

EPCIS 2.0 Event Types per Food Traceability

EventoQuando UsarloEsempio FoodCTE FSMA 204
ObjectEvent Azione su singolo oggetto/lotto Produzione lotto, ricezione, spedizione Growing, Receiving, Shipping
AggregationEvent Aggregazione (pallet, contenitore) Imballo forme DOP in pallet, containerizzazione Shipping (packing)
TransactionEvent Transazione commerciale Ordine acquisto, fattura, DDT N/A (supply chain commercial)
TransformationEvent Trasformazione lotto in nuovi prodotti Latte fresco trasformato in formaggio Transformation
AssociationEvent Associazione tra oggetti Associazione sensore IoT a lotto N/A
# Esempio EPCIS 2.0 JSON-LD - Evento di spedizione lotto DOP
# Formato JSON standard GS1 EPCIS 2.0 (May 2025 update)

epcis_shipping_event = {
    "@context": [
        "https://ref.gs1.org/standards/epcis/2.0.0/epcis-context.jsonld",
        {
            "ext": "https://parmigianoreggiano.it/epcis/ext/"
        }
    ],
    "type": "EPCISDocument",
    "schemaVersion": "2.0",
    "creationDate": "2025-10-15T08:30:00.000Z",
    "epcisBody": {
        "eventList": [
            {
                "type": "ObjectEvent",
                "eventTime": "2025-10-15T08:00:00.000Z",
                "eventTimeZoneOffset": "+01:00",
                "action": "OBSERVE",
                "bizStep": "shipping",          # GS1 CBV vocabulary
                "disposition": "in_transit",
                "epcList": [
                    "urn:epc:id:sgtin:8012345.067890.001",
                    "urn:epc:id:sgtin:8012345.067890.002",
                    "urn:epc:id:sgtin:8012345.067890.003"
                ],
                "readPoint": {
                    "id": "urn:epc:id:sgln:8012345.00000.DOCK-01"  # GLN dock uscita
                },
                "bizLocation": {
                    "id": "urn:epc:id:sgln:8012345.00000.WAREHOUSE"
                },
                "bizTransactionList": [
                    {
                        "type": "po",          # Purchase Order
                        "bizTransaction": "urn:epcglobal:cbv:bt:9876543210:PO-2025-1234"
                    },
                    {
                        "type": "desadv",      # Dispatch Advice (DDT)
                        "bizTransaction": "urn:epcglobal:cbv:bt:9876543210:DDT-2025-5678"
                    }
                ],
                "sourceList": [
                    {
                        "type": "owning_party",
                        "source": "urn:epc:id:pgln:8012345.00000"  # Consorzio PR
                    }
                ],
                "destinationList": [
                    {
                        "type": "owning_party",
                        "destination": "urn:epc:id:pgln:4099999.00000"  # Retailer
                    }
                ],
                "sensorElementList": [
                    {
                        "sensorMetadata": {
                            "time": "2025-10-15T08:00:00.000Z",
                            "deviceID": "urn:epc:id:giai:8012345.0.SENSOR-TEMP-01",
                            "deviceMetadata": "https://sensors.parmigianoreggiano.it/models/T1"
                        },
                        "sensorReport": [
                            {
                                "type": "Temperature",
                                "value": 3.8,
                                "uom": "CEL",       # Celsius (UN/CEFACT unit code)
                                "minValue": 3.2,
                                "maxValue": 4.1
                            },
                            {
                                "type": "AbsoluteHumidity",
                                "value": 82.5,
                                "uom": "A93"        # Percent relative humidity
                            }
                        ]
                    }
                ],
                "ext:lotNumber": "PR2025A001",
                "ext:dop_certificate": "DOP-IT-PR-2025-001234"
            }
        ]
    }
}

# Invio a EPCIS 2.0 Repository (es. IBM Food Trust, OPTEL, TraceLink)
import httpx

async def publish_epcis_event(event: dict, epcis_endpoint: str, api_key: str):
    """Pubblica evento EPCIS 2.0 su repository conforme"""
    async with httpx.AsyncClient() as client:
        resp = await client.post(
            f"{epcis_endpoint}/events",
            json=event,
            headers={
                "Content-Type": "application/ld+json",
                "GS1-EPCIS-Version": "2.0",
                "Authorization": f"Bearer {api_key}"
            }
        )
        resp.raise_for_status()
        return resp.json()

Case Study: Parmigiano Reggiano DOP e la Tracciabilita Blockchain

Il Consorzio del Parmigiano Reggiano rappresenta il caso studio più avanzato di tracciabilita digitale nella filiera DOP italiana. Con oltre 3.600 allevamenti bovini, 311 caseifici produttori, 1.200 stagionatori e un valore produzione di 3 miliardi di euro annui (2024), la sfida della tracciabilita e di scala industriale con requisiti di autenticita legalmente vincolanti.

Il Sistema p-Chip: Blockchain Fisica nelle Forme

Dal 2022, il Consorzio ha avviato un programma con Kaasmerk Matec e p-Chip Corporation per integrare nella storica placca di caseina (il sistema di identificazione usato dal 2002) un micro-transponder crittografico. Il p-Chip ha dimensioni inferiori a un granello di sale, e resistente a temperature da -40C a +300C, agli acidi del formaggio e ai cicli di stagionatura lunghi fino a 36 mesi. Ogni chip contiene un identificatore crittografico univoco che viene ancorato a un registro blockchain, creando un "gemello digitale" della forma.

Architettura Sistema Tracciabilita Parmigiano Reggiano DOP

FaseTecnologiaDati RegistratiAttori
Allevamento BDN (banca dati nazionale bovini), IoT Identificativo bovino, alimentazione, benessere animale, Comune provenienza latte Allevatori registrati Consorzio
Caseificio p-Chip applicato a placca caseina, RFID HF ID forma, data produzione, caseificio, matricola MiPAAF, quantità latte, parametri HACCP 311 caseifici DOP
Esame qualità Marchiatura a fuoco + p-Chip verifica Superamento esame perito (12 mesi), grado qualità, marchio DOP Perito Consorzio
Stagionatura Sensori temperatura/umidita, RFID tracking Temperatura, umidita, durata stagionatura, movimentazioni 1.200 stagionatori
Porzionatura QR Code GS1 Digital Link su confezione Lotto forma origine, data confezionamento, tracciabilita DOP Porzionatori autorizzati
Consumatore Scan QR/NFC con smartphone Visualizzazione storia completa: allevamento, caseificio, stagionatura Consumatore finale

ROI e Impatto Economico

Risultati Programma Tracciabilita Digitale Parmigiano Reggiano DOP

  • Riduzione contraffazione: Il "Italian Sounding" (prodotti che imitano PR fuori dall'area DOP) costa al Consorzio ~2.2 miliardi EUR/anno di mancato fatturato. La tracciabilita digitale rende verificabile l'autenticita a qualsiasi punto della catena distributiva globale, particolarmente negli USA, Canada e Australia dove l'Italian Sounding e più diffuso.
  • Tempi di recall: In caso di allerta sanitaria, il sistema riduce il tempo di identificazione lotti a rischio da 3-5 giorni (con registri cartacei) a meno di 2 ore.
  • Premium di prezzo: La tracciabilita verificabile consente di giustificare prezzi premium di 15-25% nei mercati internazionali dove il consumatore ha strumenti per verificare l'autenticita.
  • Accesso mercati: La compliance digitale facilità l'accesso a GDO internazionale (Whole Foods, Waitrose, Monoprix) che richiedono sempre più spesso documentazione digitale della filiera.
  • Costo implementazione: Stimato 8-15 EUR per forma nei primi anni (capex + opex), con breakeven previsto entro 3-4 anni grazie ai benefici di premium pricing e riduzione recall.

Anti-Pattern e Rischi: La Verita sulla Blockchain Alimentare

L'entusiasmo per la blockchain nella food traceability ha generato molti progetti sovradimensionati, mal progettati o direttamente falliti. IBM Food Trust, il prodotto di riferimento basato su Hyperledger Fabric, ha annunciato la chiusura del servizio nel dicembre 2022 (poi prorogata), rivelando le difficolta di business model nell'ecosistema blockchain food. Capire gli anti-pattern e fondamentale per progettare sistemi che funzionano davvero.

I 7 Anti-Pattern della Blockchain Food Traceability

  • Garbage In, Garbage Out (il più pericoloso): La blockchain garantisce l'immutabilita dei dati inseriti, non la loro correttezza. Se un operatore disonesto inserisce dati falsi (prodotto certificato BIO che non lo e), la blockchain li certifica immutabilmente come veri. La tracciabilita senza verifica on-site o sensori IoT indipendenti e una protezione illusoria.
  • Blockchain come database: Usare blockchain per archiviare i dati completi di tracciabilita (immagini, documenti, misurazioni continue) e inefficiente e costoso. La blockchain deve contenere solo hash crittografici e metadata minimi. I dati veri stanno nei database tradizionali.
  • Sovrastimare la complessità del consensus: Molte aziende hanno implementato blockchain public (Ethereum) per supply chain alimentari, pagando gas fee imprevedibili e soffrendo di latenza per ogni operazione. Per supply chain food, le blockchain permissioned (Fabric, Quorum) sono quasi sempre la scelta corretta.
  • Ignorare l'interoperabilità: Una blockchain solo interna vale poco. Il valore emerge quando tutti gli attori della filiera (produttore, logista, retailer) condividono lo stesso ledger. Senza standard aperti (EPCIS 2.0), si creano isole digitali costose.
  • Sottostimare i costi di onboarding: Il costo reale non e la blockchain in se, ma portare 50-100 fornitori SME (spesso aziende agricole con limitata digitalizzazione) sulla piattaforma. Budget conservativo: 1.500-5.000 EUR per fornitore in formazione e integrazione.
  • Mancanza di governance del consorzio: Chi gestisce i nodi? Chi approva nuovi partecipanti? Chi paga l'infrastruttura? Senza una governance chiara del consorzio blockchain, i progetti si bloccano sulle dinamiche di potere tra attori concorrenti della stessa filiera.
  • Dimenticare il consumatore: La tracciabilita esiste per creare valore. Se il consumatore non può accedere facilmente alle informazioni (QR code illeggibile, web app lenta, dati tecnici incomprensibili), l'investimento non si traduce in premium di mercato.

Schema Database PostgreSQL per Sistema di Tracciabilita

-- Schema PostgreSQL per sistema di tracciabilita alimentare
-- Compatibile con EPCIS 2.0 e FSMA 204 CTE/KDE requirements

-- Estensione per UUID e JSONB
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS pgcrypto;

-- Tabella produttori (GS1 GLN)
CREATE TABLE producers (
    gln            VARCHAR(13) PRIMARY KEY,    -- GS1 Global Location Number
    name           VARCHAR(200) NOT NULL,
    country        CHAR(2) NOT NULL,           -- ISO 3166-1 alpha-2
    region         VARCHAR(100),
    certifications JSONB DEFAULT '[]',
    contact_email  VARCHAR(200),
    created_at     TIMESTAMPTZ DEFAULT NOW()
);

-- Tabella lotti alimentari (core entity)
CREATE TABLE food_lots (
    lot_id         VARCHAR(50) PRIMARY KEY,
    product_gtin   CHAR(14) NOT NULL,
    producer_gln   VARCHAR(13) REFERENCES producers(gln),
    product_name   VARCHAR(200) NOT NULL,
    production_date DATE NOT NULL,
    expiry_date     DATE NOT NULL,
    quantity_kg     DECIMAL(12, 3),
    origin_country  CHAR(2),
    certifications  JSONB DEFAULT '[]',
    raw_materials   JSONB DEFAULT '[]',        -- Array di lot_id input + quantità
    lot_status      VARCHAR(20) DEFAULT 'ACTIVE'
                    CHECK (lot_status IN ('ACTIVE','IN_TRANSIT','STORED','SOLD','RECALLED','EXPIRED')),
    data_hash       CHAR(64),                  -- SHA-256 dei dati per ancoraggio blockchain
    blockchain_tx   VARCHAR(66),               -- Hash TX blockchain (0x + 64 hex)
    created_at      TIMESTAMPTZ DEFAULT NOW(),
    updated_at      TIMESTAMPTZ DEFAULT NOW(),

    CONSTRAINT valid_dates CHECK (expiry_date > production_date)
);

-- Indici per query frequenti
CREATE INDEX idx_food_lots_gtin ON food_lots(product_gtin);
CREATE INDEX idx_food_lots_producer ON food_lots(producer_gln);
CREATE INDEX idx_food_lots_status ON food_lots(lot_status);
CREATE INDEX idx_food_lots_production_date ON food_lots(production_date);

-- Tabella eventi di tracciamento (Critical Tracking Events - FSMA 204)
CREATE TABLE trace_events (
    event_id           UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    lot_id             VARCHAR(50) NOT NULL REFERENCES food_lots(lot_id) ON DELETE RESTRICT,
    event_type         VARCHAR(30) NOT NULL
                       CHECK (event_type IN (
                           'CREATED','SHIPPED','RECEIVED','TRANSFORMED',
                           'STORED','SOLD','RECALLED','SAMPLED','INSPECTED'
                       )),
    -- Key Data Elements (KDE) FSMA 204
    actor_gln          VARCHAR(13),
    actor_name         VARCHAR(200) NOT NULL,
    location_name      VARCHAR(200) NOT NULL,
    location_gln       VARCHAR(13),
    latitude           DECIMAL(9, 6),
    longitude          DECIMAL(9, 6),
    -- Parametri ambientali (cold chain / HACCP)
    temperature_celsius DECIMAL(5, 2),
    humidity_percent    DECIMAL(5, 2),
    quantity_kg         DECIMAL(12, 3),
    -- Documenti associati (DDT, fatture, certificati)
    document_refs       JSONB DEFAULT '[]',
    -- Dati liberi addizionali
    additional_data     JSONB DEFAULT '{}',
    notes               TEXT,
    data_hash           CHAR(64),
    blockchain_tx       VARCHAR(66),
    created_at          TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_trace_events_lot_id ON trace_events(lot_id);
CREATE INDEX idx_trace_events_type ON trace_events(event_type);
CREATE INDEX idx_trace_events_created ON trace_events(created_at);
CREATE INDEX idx_trace_events_location ON trace_events(location_gln);

-- Tabella alert HACCP automatici
CREATE TABLE haccp_alerts (
    alert_id       UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    lot_id         VARCHAR(50) REFERENCES food_lots(lot_id),
    sensor_id      VARCHAR(100),
    alert_type     VARCHAR(50) NOT NULL,  -- TEMP_HIGH, TEMP_LOW, HUMIDITY, etc.
    measured_value DECIMAL(8, 3),
    threshold_value DECIMAL(8, 3),
    alert_message  TEXT,
    resolved       BOOLEAN DEFAULT FALSE,
    resolved_at    TIMESTAMPTZ,
    created_at     TIMESTAMPTZ DEFAULT NOW()
);

-- View per genealogia lotto (query ottimizzata)
CREATE OR REPLACE VIEW lot_genealogy AS
SELECT
    fl.lot_id,
    fl.product_name,
    fl.product_gtin,
    p.name AS producer_name,
    p.country AS producer_country,
    fl.production_date,
    fl.expiry_date,
    fl.lot_status,
    fl.certifications,
    fl.raw_materials,
    fl.blockchain_tx,
    json_agg(
        json_build_object(
            'event_id',    te.event_id,
            'event_type',  te.event_type,
            'actor_name',  te.actor_name,
            'location',    te.location_name,
            'timestamp',   te.created_at,
            'temperature', te.temperature_celsius,
            'humidity',    te.humidity_percent,
            'blockchain',  te.blockchain_tx
        ) ORDER BY te.created_at
    ) AS events
FROM food_lots fl
LEFT JOIN producers p ON fl.producer_gln = p.gln
LEFT JOIN trace_events te ON fl.lot_id = te.lot_id
GROUP BY fl.lot_id, fl.product_name, fl.product_gtin,
         p.name, p.country, fl.production_date, fl.expiry_date,
         fl.lot_status, fl.certifications, fl.raw_materials, fl.blockchain_tx;

Guida ai Costi: Implementazione per PMI Alimentare

La domanda più frequente delle PMI alimentari italiane e: "quanto costa implementare un sistema di tracciabilita digitale?". La risposta dipende fortemente dalla scala, dalla complessità della filiera e dal livello di ambizione tecnologica. Ecco una guida realistica basata su progetti implementati nel 2024-2025.

Costi Implementazione Tracciabilita per Livello di Maturita

LivelloScenarioTecnologieCapex SetupOpex/AnnoTimeline
Base Piccolo produttore DOP, 1 stabilimento, 10-50 lotti/mese QR Code GS1, software SaaS tracciabilita, barcode scanner 3.000-8.000 EUR 1.200-3.600 EUR 1-2 mesi
Intermedio Cooperativa, 5-20 stabilimenti, 500-2.000 lotti/mese RFID HF/UHF + QR GS1 Digital Link + IoT temperatura + API EPCIS 40.000-120.000 EUR 8.000-24.000 EUR 4-8 mesi
Avanzato Consorzio DOP, 50+ produttori, filiera completa Tutto il precedente + Blockchain Hyperledger Fabric, cold chain IoT, consumer app 200.000-800.000 EUR 50.000-150.000 EUR 12-24 mesi
Enterprise Grande GDO, supply chain globale multistakeholder IBM Food Trust / piattaforma custom, RFID full-coverage, AI analytics 1M-5M EUR 200.000-500.000 EUR 18-36 mesi

RFID vs NFC vs QR Code: Quando Usare Cosa

CriterioQR Code GS1NFCRFID UHFRFID HF
Costo per unita Minimo (solo stampa) Basso (0.20-2 EUR) Minimo (0.05-0.30 EUR) Medio (0.30-1.50 EUR)
Velocita lettura 1 alla volta 1 alla volta 1.000+ simultaneous 10-100 simultaneous
Consumer-facing Ottimo Ottimo Non adatto Limitato
Logistica automatizzata Non adatto (line-of-sight) Non adatto Eccellente Buono
Resistenza ambienti umidi Scarsa (carta bagnata) Buona Scarsa (effetto acqua) Buona
Anti-contraffazione Bassa (clonabile) Media (NDEF firmato) Bassa Alta (p-Chip, Crypto tag)
Raccomandato per Packaging consumer, DL Prodotti premium, DOP Pallet, magazzino, logistics Prodotti singoli, farmaci, DOP

Incentivi e Finanziamenti: Come le PMI Alimentari Possono Accedere a PNRR e Transizione 5.0

L'implementazione di sistemi di tracciabilita digitale avanzata rientra in diverse misure di incentivazione disponibili per le imprese alimentari italiane nel 2025-2026. Conoscere gli strumenti disponibili può ridurre significativamente il costo netto dell'investimento.

Principali Strumenti di Incentivazione per Tracciabilita Digitale Alimentare

  • Transizione 5.0 (PNRR): Il credito d'imposta fino al 45% per investimenti in digitale e sostenibilità include esplicitamente sistemi IoT, sensori e software per tracciabilita. Il budget totale di 6.3 miliardi EUR e accessibile tramite GSE. Attenzione: serve interconnessione con sistema gestionale aziendale e documentazione tecnica dettagliata.
  • Bando Smart Agrifood (MISE/MITE): Finanziamenti a fondo perduto (30-50% delle spese ammissibili) per progetti di digitalizzazione nelle filiere agroalimentari. Include hardware RFID, IoT e sviluppo software custom.
  • PSR Regionali (Piano Sviluppo Rurale): Misura 4.2 supporta investimenti in trasformazione e commercializzazione prodotti agricoli, inclusa la digitalizzazione della tracciabilita. Cofinanziamento UE + Regione tipicamente 40-65% delle spese eleggibili per microimprese e PMI.
  • Horizon Europe (Cluster 6): Per consorzi di ricerca e innovazione, finanziamenti europei fino al 100% per progetti di ricerca su food traceability con partner universitari o centri di ricerca.
  • Sabatini Green: Finanziamento agevolato per investimenti in tecnologie digitali (inclusi RFID reader, gateway IoT, server) con contributo in conto interessi del 3.575% su finanziamento massimo 4 milioni EUR.

Roadmap di Implementazione: 12 Mesi per un Sistema Enterprise

Piano Implementazione Tracciabilita Alimentare - Approccio Incrementale

FasePeriodoAttivitaOutput
Fase 0: Assessment Mese 1 Mappatura filiera, analisi normativa applicabile, identificazione CTE, gap analysis Business Case, Architecture Blueprint
Fase 1: Fondamenta Dati Mesi 2-3 Setup PostgreSQL, schema database, API base, GS1 company prefix, GTIN assegnazione Database operativo, API v1
Fase 2: Identificazione Mesi 3-4 Deploy RFID reader punti chiave, stampa QR Code GS1 DL, test end-to-end Sistema ID operativo su 1 linea
Fase 3: IoT Cold Chain Mesi 4-6 Deploy sensori temperatura, gateway MQTT, dashboard Grafana, alert HACCP Monitoraggio continuo cold chain
Fase 4: Blockchain Mesi 6-9 Setup Hyperledger Fabric (o SaaS), onboarding partner filiera, chaincode deploy Trust layer blockchain operativo
Fase 5: Consumer Mesi 9-11 Web app consumer (scan QR), localizzazione multilingua, analytics utilizzo Portale consumer live
Fase 6: Scale Mesi 11-12 Onboarding ulteriori fornitori, ottimizzazione performance, EPCIS 2.0 compliance Sistema full-scale production-ready

Conclusioni: La Tracciabilita Come Vantaggio Competitivo

La tracciabilita alimentare digitale ha superato la fase di sperimentazione e innovazione: nel 2025 e una necessità competitiva e, per molte categorie merceologiche, un obbligo normativo imminente. Il mercato globale che supera i 23 miliardi di dollari nel 2025, la direttiva FSMA 204 con compliance prorogata al 2028 (ma inevitable) e il Digital Product Passport europeo delineano un futuro in cui ogni prodotto alimentare avra una storia digitale verificabile.

La tecnologia e matura: RFID UHF a 5 centesimi di euro per tag, QR Code GS1 Digital Link accessibili a chiunque, blockchain permissioned Hyperledger Fabric open-source, sensori BLE per cold chain a 20-30 euro l'uno. La sfida vera non e tecnologica: e di governance (chi coordina la filiera?), di onboarding (come coinvolgere le PMI meno digitalizzate?) e di business model (chi paga, chi beneficia?).

Per le PMI italiane, il caso Parmigiano Reggiano e un modello replicabile: iniziare con un consorzio che coordina l'implementazione, accedere agli incentivi PNRR e PSR, scegliere un approccio incrementale che porta valore già nella fase 1 (identificazione digitale di lotto) e aggiunge strati di sofisticazione nel tempo. Il ROI non e immediato, ma la combinazione di premium pricing, riduzione costi recall e accesso a nuovi mercati rende l'investimento economicamente sostenibile entro 3-5 anni.

Link Utili e Risorse

Prossimo Articolo della Serie FoodTech

Nel prossimo articolo della serie esploreremo Computer Vision per Controllo qualità Alimentare con PyTorch e YOLO: come implementare sistemi di ispezione visiva automatizzata per rilevare difetti in prodotti alimentari su linea di produzione, con architettura real-time, pipeline di training e deploy su hardware industriale.

Continua a seguire la serie FoodTech su federicocalo.dev per tutti i prossimi approfondimenti tecnici.

Commenti

Caricamento commenti...

Accedi per commentare

Discuti questo articolo nella community

Unisciti al gruppo Telegram per discutere con altri sviluppatori, fare domande e condividere le tue esperienze.

Ti è piaciuto questo articolo?

Esplora altri contenuti sul blog o scopri i miei progetti