Scheda I/O con sw per controllo luci e configurabile via Web

Parte 1/4

Introduzione

In un vecchio topic da me aperto (Come reperire fondi per certificazione scheda) ad un certo punto scrissi:

Diego67:
Se non trovassi il modo di far fruttare l'idea, visto che è possibile farlo con minime modifiche, adatterò la scheda in modo che non violi il punto 15 del regolamento e descriverò, se mi sarà concesso, il progetto nel forum nella speranza che tutto il lavoro fatto per arrivare all'oggetto finito possa almeno essere di aiuto/spunto a quanti ne avessero bisogno. Spero solamente che, in questa ipotesi, non ci sia qualcuno che con le giuste capacità e conoscenze non ne approfitti....

Come volevasi dimostrare non sono nato per essere un venditore e non ho grande spirito imprenditoriale. Nessuna delle ditte contattate tramite i form presenti sui rispettivi siti si è fatta viva. Forse ho sbagliato i destinatari o i canali utilizzati ma più probabilmente quanto loro proposto non ha destato alcun interesse e forse questo deriva dalla mia presunzione di aver creato qualcosa di utile. Mi trovo quindi, come promesso a suo tempo e prima di dimenticarmi tutto, a presentarvi il circuito da me progettato, costruito e testato.

Descrizione Hardware


Come visibile dalla foto si tratta di una schedina dotata di 8 ingressi ed 8 uscite gestite da un Esp32 Pico D4 ( rispetto al normale esp32 la versione Pico integra all’interno del chip anche la memoria flash e l’oscillatore ). Le uscite possono pilotare carichi sia in alternata ( 24V ) che in continua. Nel caso dell’alternata ho testato la scheda con dei fototriac ( AQH3213 max 1,2A ) mentre nel caso di uscite in continua l’ho provata con dei driver per mosfet FOD3182 capaci di pilotare correnti di picco di 3A. Ogni uscita è protetta da un fusibile zoccolato e da un varistore. Anche gli ingressi ( tutti opto isolati ) possono essere comandati sia in alternata che in continua e in modo indipendente l’uno dall’altro. Per l’alimentazione della scheda ho utilizzato un modulino da 5V 3W alimentandolo a 24V AC. ( Risulta utilizzato fuori range per la tensione di ingresso ma funziona ). Completa la dotazione di bordo un RTC con batteria tampone. Ho utilizzato un DS3231MZ versione 4+4 pin del più noto DS3231M e devo dire che, pur essendo stato acquistato in Cina, mi ha stupito per la precisione.
Ho voluto contenere le dimensioni della scheda a 10X10 in quanto si trattava del mio primo circuito in smd ed il mio primo stampato ordinato online. Ho cercato quindi di limitare al massimo le spese per non dover, in caso di mancato funzionamento, buttare al vento troppi soldi ( fino a 10X10 -> 5€ + trasporto per 10 circuiti; oltre quelle dimensioni si parte da 35€ + trasporto sempre per 10 stampati). Con mia sorpresa, a parte dei falsi contatti iniziali in alcuni ingressi provocati dal flussante non pulito bene e risolti con una bella lavata con alcol Isopropilico, tutto è funzionato regolarmente. Logicamente col senno di poi qualche variazione allo stampato l’avrei fatta ma per testarne il funzionamento può andar bene anche così.

Schema elettrico
Lo schema elettrico di seguito riportato può apparire complesso ma lo è solamente per la moltiplicazione x 8 di ogni stadio di ingresso ed uscita. Guardando un singolo stadio penso risulti abbastanza banali per chiunque. Una cosa che evidenzio è l’utilizzo per ogni stadio d’ingresso del fotoaccoppiatore LTV-354T ( sostituibile con PC364NJ0000F ). Entrambi hanno in ingresso due fotodiodi contrapposti e sono utilizzabili quindi anche con tensioni d’ingresso alternate senza costringere ad inserire un diodo esterno per proteggere il fotodiodo interno dalle tensioni inverse. E’ possibile inoltre, anche con tensioni continue, comandare gli ingressi in entrambi i versi cosa non fattibile con fotoaccoppiatori con un solo fotodiodo. Entrambi i fotoaccoppiatori citati hanno un CTR (Current Transfer Ratio) del 400% e quindi per far saturare il fototransistor di uscita basta una corrente sui fotodiodi maggiore di ¼ della corrente di collettore. Nell’ipotesi di saturazione del transistor la corrente sul diodo led in uscita (D11-D18) sarà inferiore ai 2 mA. Basterebbe quindi una corrente sui fotodiodi di 0,5 mA per farlo saturare. Per stare sul sicuro, mantenendo basse dissipazioni, ho dimensionato le resistenze per correnti di ingresso di circa 1mA. I Diodi led (D11-D18) oltre che per un’indicazione visiva del cambio stato in ingresso hanno il compito di mantenere la tensione agli ingressi dell’ESP32 entro i range prestabiliti. Ricordo che l’ESP32 viene alimentato con 3,3V. La presenza del led bianco fissa la tensione massima in ingresso dell’ESP a circa 2,5V che viene interpretata come un 1 logico dall’ESP. Con il fototransistor interdetto la resistenza da 10k in parallelo al led si occupa di scaricare il condensatore e di tenere basso l’ingresso dell’ESP. Il condensatore in parallelo a resistenza e led serve invece ad evitare che possibili disturbi sugli ingressi facciano recepire livelli alti agli ingressi dell’ESP. Il suo valore l’ho dimensionato in modo che venga recepita la variazione di stato di un ingresso quando questa dura almeno 200ms.


Passiamo ora allo stadio di uscita. Come già detto a seconda dell’utilizzo possono essere usati normali fotoaccopiattori , foto triac o driver isolati per mosfet. In ogni caso nel circuito da me proposto il fotodiodo all’ ingresso di uno di questi componenti si troverà in serie ad un led indicatore e ad una resistenza limitatrice. Perché la somma della tensione sul fotodiodo e di quella ai capi del diodo led indicatore non superasse la tensione di uscita massima dell’ESP32 ho dovuto scegliere un led rosso essendo, tra i vari colori, quello con la minor tensione ai suoi capi. Nonostante questo, il delta tra la tensione di uscita dall’ESP e la somma delle tensioni tra led e fotodiodo risulta minimo. Per questo il valore della resistenza limitatrice è di soli 24 Ohm.
Il blocchetto giallo nella parte bassa centrale dello schema elettrico è quello relativo all’ RTC. Nel mio caso, come detto nell’ introduzione, ho utilizzato il DS3231MZ che colloquia con l’ESP tramite I2C e che, in caso di mancanza di alimentazione esterna, viene tenuto in vita dalla batteria tampone presente sulla scheda. Attualmente Il posizionamento dell’RTC è sotto l’ESP32 e questo permetterebbe, oltre alle classiche funzioni, di avere una stima della temperatura del circuito utilizzando il sensore di temperatura interno all’RTC stesso. Per ora questa funzione di controllo non l’ho sfruttata nel software ma mi limito a fare una stampa della temperatura sul monitor seriale dell’Arduino IDE.
Il partitore resistivo formato da R10 ed R12 serve per tenere sotto controllo la tensione di alimentazione. La giunzione delle due resistenze viene collegata ad un ingresso dell’ESP configurato come ingresso analogico e quando la tensione da questo rilevata va sotto un certo limite viene eseguita una funzione di salvataggio dei dati dinamici in “EEProm”. Logicamente questo valore di controllo non può essere impostato troppo basso perché altrimenti il micro non avrebbe più energia per effettuare il salvataggio.

Continua.....

Parte 2/4

Descrizione funzioni


Pur essendo la scheda utilizzabile per diversi utilizzi il software installato è specifico per controllare un sistema casalingo di illuminazione tramite controllo diretto delle lampadine se a bassa tensione e con potenze max di 20W per uscita o tramite interposizione di relè comandati in bassa tensione ed installati su di apposito quadro da elettricista abilitato nel caso di tensioni /potenze superiori.

Ogni ingresso può essere programmato per comandare le uscite come:
• passo passo
• passo passo gruppo
• passo passo sequenze multiple
• passo passo sequenze multiple SetAll ResetAll
• Timer riarmabile
• Timer riarmabile con preavviso
• Timer resettabile
• Timer resettabile con preavviso.

Per ogni ingresso è possibile scegliere su quali uscite opererà il comando.
Oltre a queste funzioni ogni uscita o gruppo di uscite possono essere comandate non dagli ingressi ma dall’RTC presente sulla scheda (funzione Timer). È possibile cioè, tramite la creazione di una riga su di una tabella, a livello settimanale scegliere in quali giorni/ore/minuti e secondi attivare le uscite e il tempo che devono restare attive. È anche possibile scegliere, sempre sulla stessa riga, una specie di intermittenza stabilendo il tempo On, il tempo Off ed il numero di ripetizioni. Il numero di diverse righe (eventi temporizzati) programmabile può arrivare al centinaio.


Come vedete quanto proposto è solamente un piccolo sottoinsieme di quanto cinesi & C offrono da tempo e meglio ingegnerizzate ma la possibilità di gestire 8 ingressi/uscite con un’unica scheda non è così diffuso.
Altra differenza con le varie Sonoff è che queste permettono di comandare un apparecchio anche da remoto mentre la mia scheda non lo permette. Questo può essere visto come un grosso limite ma l’ho voluto per 3 ragioni principali:
1- Per sicurezza non mi piace che sia un server remoto gestito da sconosciuti a comandare qualsiasi cosa a casa mia anche se in seguito a mia richiesta.
2- Per non aumentare la quantità di onde elettromagnetiche in cui siamo immersi non voglio avere trasmissioni wireless sempre attive.
3- Per comodità voglio che il comando sia possibile anche con normali pulsanti molto più amichevoli, per molte persone, rispetto ad una App e funzionanti anche nel caso di assenza di Internet come può essere la rampa delle scale di un condominio.
La scheda risulta programmabile tramite pagine web gestite dall’ esp32 fatto funzionare come access point con IP e credenziali direttamente modificabili da pagina web.

Dopo un tempo di inattività prestabilito l’access point, se non disconnesso forzatamente, automaticamente si disabilita. La configurazione della scheda viene salvata nella flash del micro utilizzata come se fosse una EEProm. La tensione a valle dell’alimentatore viene ciclicamente controllata da uno dei due core del micro e se scende sotto un certo valore la “foto” dello stato delle uscite viene salvata in EEprom in modo da ripristinarne lo stato una volta ritornata l’alimentazione. Questo ripristino avverrà solo per quelle uscite per cui si è scelta questa funzionalità. In alternativa è possibile scegliere di far partire le uscite come accese o spente indipendentemente dallo stato degli ingressi.

Oltre alle funzioni descritte la scheda possiede la capacità di essere programmata via OTA e cioè senza collegamento fisico. Anche questo aggiornamento può essere fatto utilizzando la connessione generata dall’ESP32 sempre utilizzato come access point. Questa funzione risulta utile in particolare nei casi in cui la scheda venisse inserita in una scatola di derivazione o in un quadro non facilmente raggiungibile. Devo dire che questa caratteristica, funzionante senza accesso ad Internet, non è stata di facile implementazione e che senza San “Google” starei ancora cercando una soluzione.

Descritto sommariamente l’hardware presente e le funzioni implementate vediamo ora di descrivere il software che fa girare il tutto.

Descrizione software
Inizialmente generavo le pagine da codice tramite l’IDE di Arduino (client.println) ma con l’aumento delle funzioni implementate diventava disordinato e difficilmente munutenibile. Oltre a questo, non sopportavo e non sopporto, anche se impercettibile, il refresh della pagina dovuto al ricaricamento. Oltre al fastidio personale su cui si può soprassedere questo primo tipo di soluzione non è per nulla efficiente in quanto ogni più piccola variazione da apportare sulla pagina il server web deve rispedire l’intera pagina dopo averla rielaborata. Come se non bastasse potendo potenzialmente gestire tabelle di un centinaio di righe lo spazio occupato sarebbe stato troppo anche per un esp32 che di memoria ne ha parecchia. Ho pensato quindi di spostare il carico elaborativo sul client, solitamente molto più potente di un Arduino ed anche di un Esp. Per far questo ho modificato il codice facendo creare praticamente ogni oggetto presente sulle pagine da Java Script. In pratica al caricamento di una pagina e ad ogni successivo ricaricamento il server web invia al client il codice html+java script e poi ad ogni variazione sarà il client a modificare la pagina e ad inviare al server i soli dati modificati/aggiunti. Visto che la parte web serve solo per la programmazione e poi, in questa scheda, non viene più utilizzata il lavoro poteva terminare qui ma non ero ugualmente soddisfatto di quanto compiuto. Come detto più sopra restava ancora il problema del refresh visibile della pagina. In prima battuta l’ho risolto tramite Ajax che permetteva il refresh di singole porzioni di pagina ma che obbligava a un polling continuo da parte del client per verificare che il server non avesse nuovi dati da inviare. Minore era il tempo tra una verifica ed un’altra e minore era il tempo di aggiornamento della pagina in seguito alla pressione di un pulsante. A questa diminuzione corrispondeva però un maggiore ed inutile traffico in rete ed un altrettanto inutile carico sul micro. Nemmeno questo mi andava troppo bene. Fortunatamente ho scoperto l’esistenza dei WebSocket che, instaurando una comunicazione permanete e bidirezionale tra client e server, hanno permesso di risolvere anche questo non ideale comportamento. Devo dire che sono rimasto sbalordito nel vedere la velocità con cui la pressione di un pulsante collegato su Esp32 viene propagata alla pagina e viceversa la pressione di un pulsante sulla pagina si traduce con il cambio di stato su di una uscita.

Continua.....

Parte 3/4

Un’altra caratteristica che ho voluto implementare sul software della scheda è quella che somiglia, alla lontana, al concetto di Master Page implementato in .NET. Ho voluto cioè comporre le pagine con una parte comune a tutte all’interno della quale inserire un contenuto diverso per ogni pagina ( vedi parte alta di ogni pagina riportata ). Oltre a risparmiare parecchia memoria per il codice si ha il vantaggio, se serve, di dover modificare una sola volta la parte comune. Con un approccio più tradizionale ogni pagina avrebbe contenuto anche la parte comune ed una variazione su questa parte avrebbe comportato la necessità di variarla su tutte le pagine.
Per fare queste operazioni uso variabili del tipo String. So che tutti concordano sul fatto che l’utilizzo di questo tipo di dato su Arduino non sia consigliato ma ne trovo l’utilizzo molto pulito ed intuibile. Oltre a questo le dimensioni della memoria sull’Esp32 non sono così limitate come su un ATMega328 ed inoltre ho letto che sull’Esp32 gira un RTOS che si occupa anche del Garbage Collection.

#include <EEPROM.h>
#include <WiFi.h>
#include <WebServer.h>
#include <Update.h>
#include "Wire.h"
#include "VariabiliGlobali.h"
#include <WebSocketsServer.h>

TaskHandle_t Task1;

#include <DS3231M.h> 
DS3231M_Class RTC;    

WebServer httpServer(80);
WebSocketsServer webSocket = WebSocketsServer(1337);
String StringaRicevuta = "";
void onWebSocketEvent(uint8_t client_num, WStype_t type,uint8_t * payload, size_t length) 
{
  switch(type) 
  {
      case WStype_DISCONNECTED:
        Serial.printf("[%u] Disconnected!\n", client_num);
        break;
   
      case WStype_CONNECTED:
        {
          IPAddress ip = webSocket.remoteIP(client_num);
          Serial.printf("[%u] Connection from ", client_num);
          Serial.println(ip.toString());

          webSocket.broadcastTXT("StatoIngressi:" + String(StatoIngressi));
          webSocket.broadcastTXT("StatoUscite:" + String(StatoUscite));
        }
        break;
   
      case WStype_TEXT:  // quando ricevo una stringa dal client Web....
            StringaRicevuta = String((char *)payload);
            
            if (StringaRicevuta.startsWith("StatoIngressiWeb:"))
              { 
                  StringaRicevuta.replace("StatoIngressiWeb:","");
                  StatoIngressiWeb = StringaRicevuta.toInt();               
              } 
            else if (StringaRicevuta.startsWith("SetTime:"))
              { 
                  StringaRicevuta.replace("SetTime:","");
                  SetTime(StringaRicevuta);             
              } 
            else if (StringaRicevuta.startsWith("SaveInConf:"))
              { 
                  StringaRicevuta.replace("SaveInConf:","");
                  Save_In_Conf(StringaRicevuta);         
              } 
            else if (StringaRicevuta.startsWith("ResetInConf:"))
              { 
                  StringaRicevuta.replace("ResetInConf:","");
                  Reset_In_Conf();         
              } 
            else if (StringaRicevuta.startsWith("SaveTimerConf:"))
              { 
                  StringaRicevuta.replace("SaveTimerConf:","");
                  Save_Timer_Conf(StringaRicevuta);          
              } 
            else if (StringaRicevuta.startsWith("ResetTimerConf:"))
              { 
                  StringaRicevuta.replace("ResetTimerConf:","");
                  Reset_Timer_Conf();         
              }  
            else if (StringaRicevuta.startsWith("SaveAPConf:"))
              { 
                  StringaRicevuta.replace("SaveAPConf:","");
                  Save_AP_Conf(StringaRicevuta);            
              } 
            else if (StringaRicevuta.startsWith("ResetAPConf:"))
              { 
                  StringaRicevuta.replace("ResetAPConf:","");
                  Reset_AP_Conf();          
              }
            else 
              {
                  Serial.println("[%u] Message not recognized");
              }
            break;
            
      Serial.print(" StringaRicevuta.replace : "); Serial.println(StringaRicevuta); 
      
      case WStype_BIN:
      case WStype_ERROR:
      case WStype_FRAGMENT_TEXT_START:
      case WStype_FRAGMENT_BIN_START:
      case WStype_FRAGMENT:
      case WStype_FRAGMENT_FIN:
      default:
        break;
  }
}

String MasterPage = "<!DOCTYPE html PUBLIC '-//W3C//DTD ……… (5.077 byte) ..…..</div></body></html>";

void PaginaHome() 
  {
    String NewPage = MasterPage;
    NewPage.replace("<!--|Contenuto Pagina| -->","<div><div><table…... (4.065 byte) ……);}</script>");
    httpServer.send(200, "text/html", NewPage); 
  }

void PaginaOTA() 
  {
    String NewPage = MasterPage;
    NewPage.replace("<!--|Contenuto Pagina| -->","<div class='St3'><……… (2.411 byte) …. script>");
    httpServer.send(200, "text/html", NewPage); 
  }

void PaginaSSR_Array() 
  {
    String NewPage = MasterPage;
    ConfigurazioneIngressi = Get_In_Conf();    
    NewPage.replace("<!--|Contenuto Pagina| -->","<script type='text/javascript'……(7.315 byte) …….;}}</script>");
    httpServer.send(200, "text/html", NewPage); 
  }

void PaginaAccessPoint() 
  {
    String NewPage = MasterPage;
    ConfigurazioneAP = Get_AP_Conf(&AP); 
    NewPage.replace("<!--|Contenuto Pagina| -->","<script type='text/javascript' ……… (2.240 byte) ….}</script>");
    httpServer.send(200, "text/html", NewPage);   
  }

void PaginaTimer() 
  {   
    String NewPage = MasterPage;
    ConfigurazioneTimer = Get_Timer_Conf(); 
    NewPage.replace("<!--|Contenuto Pagina| -->","<script type='text/…… (6.687 byte) ..….}</script>");
    httpServer.send(200, "text/html", NewPage); 
  }

void PaginaNonTrovata() 
  {
    httpServer.send(404, "text/plain", "Pagina non trovata");
    UltimaPaginaAperta = "Errore";
  }

void Codice_Task1( void * pvParameters )
  {
    Serial.print("Task1 running on core ");
    Serial.println(xPortGetCoreID());
    
    for(;;)
      {
        uint16_t ValoreAlimentazione = analogRead(PinControlloAlimentazione);
        if ( ValoreAlimentazione < ValoreMinimaTensioneAlimentazione ) // Limite tensione sotto alla quale salvare le variabili in EEProm
          {
            SalvaParametriInEEProm_PerMancanzaAlimentazione(); 
            Serial.print("ValoreAlimentazione = ");Serial.println(ValoreAlimentazione);           
          } 
        Verifica_BlinkLed_NoDelay();
      } 
  }


void setup()
{
  for (uint8_t Cont = 0; Cont < 8; pinMode(PinIn[Cont++], INPUT_PULLDOWN));
  for (uint8_t Cont = 0; Cont < 8; pinMode(PinOut[Cont++], OUTPUT));
   pinMode(PinControlloAlimentazione, INPUT);
  pinMode(PinReset, INPUT_PULLUP);
  pinMode(PinStatusLed, OUTPUT);
  // Altro codice non riportato

}

void loop() 
{
  webSocket.loop();
  httpServer.handleClient();
  // Altro codice non riportato
        // Se un pulsante d'ingresso viene premuto per almento 6 secondi .......
            {
                WiFi.mode(WIFI_AP);
                delay(200);
                boolean EsitoConf = WiFi.softAPConfig(AP.IP, AP.Gateway, AP.Subnet);
                delay(200);
                boolean EsitoAtt = WiFi.softAP(AP.ssid.c_str(), AP.password.c_str());        
                delay(200);
              
                if (EsitoConf == true && EsitoAtt == true) Serial.println("Ready"); else Serial.println("Failed!");
              
                IPAddress myIP = WiFi.softAPIP();
                Serial.print("AP IP address: "); Serial.println(myIP); Serial.println("HTTP server started");
              
                delay(200);
                httpServer.on("/", HTTP_GET, PaginaHome);
                httpServer.on("/Home.htm", HTTP_GET, PaginaHome);
                httpServer.on("/SSR_Array.htm", HTTP_GET, PaginaSSR_Array);
                httpServer.on("/AccessPoint.htm", HTTP_GET, PaginaAccessPoint);
                httpServer.on("/Timer.htm", HTTP_GET, PaginaTimer);
                httpServer.on("/AggiornamentoOTA.htm", HTTP_GET, PaginaOTA);
                // Altro codice non riportato
                httpServer.onNotFound(PaginaNonTrovata);
              
                httpServer.begin();
                webSocket.begin();
                webSocket.onEvent(onWebSocketEvent);
            }
 // Altro codice non riportato
}

Continua…

Parte 4/4

Lato pagine HTML la parte principale di codice JavaScript deputata allo scambio di dati col Server Web (ESP32) e contenuta nella Master Page sarà del tipo:

    <script type="text/javascript" language="javascript">

        function Get(ID) {
            return document.getElementById(ID);
        }

        function wsConnect(url) {
            websocket = new WebSocket(url);
            websocket.onopen = function (evt) { onOpen(evt) };
            websocket.onclose = function (evt) { onClose(evt) };
            websocket.onmessage = function (evt) { onMessage(evt) };
            websocket.onerror = function (evt) { onError(evt) };
        }

        function onOpen(evt) {
            console.log("Connected");
            InviaAServer("Aperta connessione tramite WebSocket");
        }

        function onClose(evt) {
            console.log("Disconnected");
            setTimeout(function () { wsConnect(url) }, 2000);
        }

        function InviaAServer(message) {
            console.log("Sending: " + message);
            websocket.send(message);
        }

        function onError(evt) {
            console.log("ERROR: " + evt.data);
        }

        function onMessage(evt) {

            let DatoRicevuto = evt.data;

            console.log("Ricevuto: " + DatoRicevuto);
            if (DatoRicevuto.includes("DataOraScheda:")) {
                Get('TDataOraScheda').innerHTML = DatoRicevuto.replace("DataOraScheda:", "");
            }
            else if (DatoRicevuto.includes("StatoIngressi:")) {
                StatoIngressi = DatoRicevuto.replace("StatoIngressi:", "");
                for (let i = 0; i < 8; i++) {
                    let StatoI = (StatoIngressi >> i) & 1;
                    if (StatoI)
                        SettaColoreButton('In_' + i);
                    else { ResettaColoreButton('In_' + i); }

                }
            }
            else if (DatoRicevuto.includes("StatoUscite:")) {
                StatoUscite = DatoRicevuto.replace("StatoUscite:", "");
                let Riga = Get("RD_Num_U");
                for (let i = 0; i < 8; i++) {
                    let StatoU = (StatoUscite >> i) & 1;
                    let Cella = Riga.cells[i];
                    if (StatoU)
                        Cella.style.backgroundColor = 'Red';
                    else { Cella.style.backgroundColor = Blu; }
                }
            }
            else if (DatoRicevuto.includes("Messaggio:")) {
                DatoRicevuto = DatoRicevuto.replace("Messaggio:", "");
                let DatiMsg = DatoRicevuto.split('|');
                Get('Msg').style.color = DatiMsg[0];
                Get('Msg').innerText = DatiMsg[1];
            }
        }

        function Home() {
            window.open('/Home.htm', '_self');
        }

    </script>

Quanto riportato fino a questo punto di sicuro contiene errori macroscopici sia a livello Hardware che software e nella migliore delle ipotesi può essere perfezionato in ogni ambito. Resto quindi disponibile per eventuali suggerimenti.
Nonostante non abbia approfondito diversi argomenti tutta la descrizione è diventata ugualmente un papiro. Probabilmente mi manca anche il dono della sintesi… Ringrazio anticipatamente quanti avranno la pazienza di leggere il tutto.

Fine

Diego67:
Nessuna delle ditte contattate tramite i form presenti sui rispettivi siti si è fatta viva. Forse ho sbagliato i destinatari o i canali utilizzati ma più probabilmente quanto loro proposto non ha destato alcun interesse e forse questo deriva dalla mia presunzione di aver creato qualcosa di utile. Mi trovo quindi, come promesso a suo tempo e prima di dimenticarmi tutto, a presentarvi il circuito da me progettato, costruito e testato.

Mi sembra un buon progetto, anche ben realizzato, mi spiace che tu non abbia trovato sbocchi.
Ma considerando l'atavica repulsione degli imprenditori italiani per tutto ciò che o non capiscono o che secondo loro non gli porterà guadagni certi ed immediati (leggasi: investimenti, questi sconosciuti...), al posto tuo proverei anche a contattare ditte estere, ovviamente iniziando da quelle europee (tedesche e francesi in primis) traducendo in inglese la tua presentazione del progetto. Hai visto mai.. :wink:

Ciao docdoc, ti ringrazio per il tuo autorevole apprezzamento e per il consiglio.