Uscita impulsiva per 3 secondi

Mi trovo a smanettare con arduino1 e shield I/O con 6 ingressi e 6 uscite a relè...

Volevo integrare allo sketch seguente una funzione che pi pemette di attivare una uscita pin per secondi in maniera impulsiva se il carattere ricevuto dalla seriale IDE è per esempio "J1"...

Mi spiego meglio :

Carattere ricevuto "O1" ------ attivo uscita 1 equivalente a Pin7
Carattere ricevuto "O1" ------ disattivo uscita 1 equivalente a Pin7 modalità PASSO-PASSO

Carattere ricevuto "J1" ------ attivo uscita 1 equivalente a Pin7 per 3 secondi e poi si spegne

Carattere ricevuto "I1" ------ leggo stato-ingresso 1 equivalente a Pin2

Lo sketch è il seguente :

// Variables will change:
int in=0;             
int out=0;
int an=0;
int inByte=0;

// the follow variables is a long because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long interval = 1000;           // interval at which to blink (milliseconds)

void setup() {
  
    // start serial port at 9600 bps:
  Serial.begin(9600);


  // set the digital pin as output:
  for (int out=8; out<=13; out++){
    pinMode(out, OUTPUT);      
  }
}

void loop()
{
  // here is where you'd put code that needs to be running all the time.

  // check to see if it's time to blink the LED; that is, if the 
  // difference between the current time and last time you blinked 
  // the LED is bigger than the interval at which you want to 
  // blink the LED.
 
  if (Serial.available() > 0) {
    protocollo();
  }
  
     
}


void protocollo()
{
  inByte = Serial.read();
    switch (inByte) 
    {
        case 79: //O  out
          
          
          Serial.println("Out number? (1 to 6)");   // send an initial string
            while (Serial.available() <= 0) 
            {
              delay(300);
            }
            
            out = Serial.read()-48;
            Serial.println("ricevuto ");
            Serial.print(out);
            if (out>=1&&out<=6)
            {
              out=out+7;
              if (!digitalRead(out))
                digitalWrite(out, HIGH);
              else
                digitalWrite(out, LOW);
                
              Serial.print("Out ");
              Serial.print(out-7);
              Serial.print(" = ");
              Serial.println(digitalRead(out));
            }
          
          break;
          
        case 73: //I
          //input
          
          Serial.println("In number? (1 to 6)");   // send an initial string
       
            while (Serial.available() <= 0) 
            {
              delay(300);
            }
            
            in = Serial.read()-48;
            if (in>=1&&in<=6){
            in=in+1;
            
            Serial.print("In ");
            Serial.print(in-1);
            Serial.print(" = ");
            Serial.println(digitalRead(in));
            }
          break;
          
          case 65: //A
          //analog
          
          Serial.println("Analog number? (1 to 6)");   // send an initial string
       
            while (Serial.available() <= 0) 
            {
              delay(300);
            }
            
            an = Serial.read()-48;
            if (an>=1&&an<=6){
            an=an-1;
            

            Serial.print("Analog ");
            Serial.print(an+1);
            Serial.print(" = ");
            Serial.println(analogRead(an));
            }
          break;

          
        //default: 
          // if nothing else matches, do the default
          // default is optional
     
    } 
 
   
}

Ora allego la modifica chiaramente con il blocco di codice mancante legato alla funzione dell'uscita impulsiva che ho realizzato con i Delay ma non mi soddisfa in quanto mi blocca il codice e ho provato con millis ma senza nessun risultato valido...un aiuto di cuore a chi volesse aiutarmi....grazie...

// Variables will change:
int in=0;             
int out=0;
int an=0;
int inByte=0;

// the follow variables is a long because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long interval = 1000;           // interval at which to blink (milliseconds)

void setup() {
  
    // start serial port at 9600 bps:
  Serial.begin(9600);


  // set the digital pin as output:
  for (int out=8; out<=13; out++){
    pinMode(out, OUTPUT);      
  }
}

void loop()
{
  // here is where you'd put code that needs to be running all the time.

  // check to see if it's time to blink the LED; that is, if the 
  // difference between the current time and last time you blinked 
  // the LED is bigger than the interval at which you want to 
  // blink the LED.
 
  if (Serial.available() > 0) {
    protocollo();
  }
  
     
}


void protocollo()
{
  inByte = Serial.read();
    switch (inByte) 
    {
        case 79: //O  out
          
          
          Serial.println("Out number? (1 to 6)");   // send an initial string
            while (Serial.available() <= 0) 
            {
              delay(300);
            }
            
            out = Serial.read()-48;
            Serial.println("ricevuto ");
            Serial.print(out);
            if (out>=1&&out<=6)
            {
              out=out+7;
              if (!digitalRead(out))
                digitalWrite(out, HIGH);
              else
                digitalWrite(out, LOW);
                
              Serial.print("Out ");
              Serial.print(out-7);
              Serial.print(" = ");
              Serial.println(digitalRead(out));
            }
          
          break;
          
        case 73: //I
          //input
          
          Serial.println("In number? (1 to 6)");   // send an initial string
       
            while (Serial.available() <= 0) 
            {
              delay(300);
            }
            
            in = Serial.read()-48;
            if (in>=1&&in<=6){
            in=in+1;
            
            Serial.print("In ");
            Serial.print(in-1);
            Serial.print(" = ");
            Serial.println(digitalRead(in));
            }
          break;

          
           case 74: //J  out IMPULSIVA
          
          
          Serial.println("Out number? (1 to 6)");   // send an initial string
            while (Serial.available() <= 0) 
            {
              delay(300);
            }
            
            out = Serial.read()-48;
            Serial.println("ricevuto ");
            Serial.print(out);
            if (out>=1&&out<=6)
            {
              out=out+7;
              if (!digitalRead(out))
                digitalWrite(out, HIGH);
           
                 delay(3000);        // uscita impulsiva a 3 secondi ma non multitasking...vorrei realizzarla col      
                                          // millis ma non sono riuscito...

                digitalWrite(out, LOW);
                
              Serial.print("Out ");
              Serial.print(out-7);
              Serial.print(" = ");
              Serial.println(digitalRead(out));
            }
          
          break;
          

          
          case 65: //A
          //analog
          
          Serial.println("Analog number? (1 to 6)");   // send an initial string
       
            while (Serial.available() <= 0) 
            {
              delay(300);
            }
            
            an = Serial.read()-48;
            if (an>=1&&an<=6){
            an=an-1;
            

            Serial.print("Analog ");
            Serial.print(an+1);
            Serial.print(" = ");
            Serial.println(analogRead(an));
            }
          break;

          
        //default: 
          // if nothing else matches, do the default
          // default is optional
     
    } 
 
   
}

Grazie...

Hai provato con le librerie di classi create da leo72?

C'è il leOS, SecTimer o simile, cerca un suo post e trovi i link alle librerie, puoi anche cercare nel playground.

Oggi di leggere codice proprio non ci riesco, mi girano gli occhi.

Ciao.

Per rispondere a Maurotec non ho provato le librerie esterne perchè volevo realizzare il tutto con un semplice millis() ma quello che sembra strano è proprio che realizzare una uscita impulsiva sembra facile ma non lo è per niente....
Ad esempio ho provato un semplice sketch dove premo un pulsante e piloto un led pin on-off per 3 secondi ma ripeto senza il delay vado in tilt...

Se qualcuno mi può aiutare con un esempio di uscita impulsiva realizzato con millis ne sarei veramente riconoscente...poi capito il concetto provo anche a buttarmi con le librerie secTimer e LeOs...
Grazie...

pietro78:
Ad esempio ho provato un semplice sketch dove premo un pulsante e piloto un led pin on-off per 3 secondi ma ripeto senza il delay vado in tilt...

Che intendi per impulsivo? Cioè tenere un'uscita alta per 3 secondi da quando premi un pulsante?

Senza una variabile di stato non lo puoi fare.

byte stato = false; 
byte pulsante = 2;
byte led = 13;
unsigned long myMillis;

void setup() {
    pinMode(led, OUTPUT);
    pinMode(pulsante, INPUT_PULLUP);
}

void loop() {
  if (!digitalRead(pulsante)) {
    delay(30); //debounce
    if (!digitalRead(pulsante)) {
      digitalWrite(led, HIGH);
      stato = true;
      myMillis = millis();
    }
  }
  if (stato) {
    if (millis() - myMillis > 3000) {
      digitalWrite(led, LOW);
      stato = false;
    }
  }
}

Come funziona il codice?
Ogni volta che premi il pulsante (uso la logica del LOW come premuto, il pulsante è tenuto alto con la pull-up interna) viene messo a TRUE il flag che controlla lo stato del led, questo viene acceso e poi viene salvato il valore attuale di millis.
Si controlla poi se lo stato del flag. Se è TRUE, si guarda se è trascorso l'intervallo voluto, 3 secondi. Se sì, si spenge il led e si rimette a FALSE lo stato.

Grazie Leo per il codice...non ancora ho modo di provare il codice ma penso che hai trovato la souzione...

Una sola domanda a scopo didattico se mi permetti ...cosa significa il punto esclamativo ??

if (!digitalRead(pulsante))

Penso che debba significare "se leggi la pressione del pulsante" ma non conoscevo l'espressione con il "!"...

pietro78:
...
Una sola domanda a scopo didattico se mi permetti ...cosa significa il punto esclamativo ??
...

E' l'operatore booleano di NEGAZIONE : && - Arduino Reference

In pratica, se la digitalRead ritorna "vero" il "!" lo inverte in "falso" e viceversa :wink:

Guglielmo

P.S. : Leo ti ha specificato che usa la logica del LOW per indicare pulsante premuto, quindi tu, nell'IF, devi rilevare la condizione di LOW

Ne approfitto per un'altra domanda didattica...siccome non ho avuto ancora modo di verificare il codice mi pare strana l'espressione:

if (stato) 
{  }

In pratica con la condizione "if(stato)" cosa si esprime ??!!

pietro78:
...
In pratica con la condizione "if(stato)" cosa si esprime ??!!

L'operatore IF è utilizzato per verificare se una certa condizione è vera e, in tal caso, eseguire quello che è specificato ... qui il riferimento : http://arduino.cc/en/Reference/If

In pratica tu hai

if (condizione_da_verificare) {

   cosa_fare_se_la_condizione_è_vera
}
else {
   cosa_fare_se_la_condizione_è_falsa
}

la parte dell' else è opzionale.

Credo comunque che sarebbe bene se dedicassi un po' di tempo alla lettura almeno di un semplice manuale di "C" e del "reference" http://arduino.cc/en/Reference/HomePage :wink:

Guglielmo

Scusa, forse ho frainteso io la domanda ... forse non avevi capito proprio cosa intendesse Leo con quell' IF ?

In tal caso ...
... "stato" come vedi è una variabile di tipo "byte" che Leo dichiara inizialmente "false" e che, se si verificano certe condizioni, mette invece a "true".

Nel successivo IF (stato) non fa altro che controllare se quella variabile è ancora a "false" (e quindi le condizioni non si sono verificate) o a "true" .... in tal caso esegue quello che c'è nell'IF ...

Altri dubbi ? :slight_smile:

Guglielmo

Guglielmo ti ha spiegato la logica che ho adoperato.
Sono tutte delle contrazioni per ridurre il quantitativo di codice da scrivere.

Bisogna conoscere Arduino e cosa fa per poi poterlo usare, ma è anche utile conoscere il suo codice per poterlo usare.
false e true sono 2 costanti predefinite che valgono rispettivamente 0 e 1.
Avrei potuto utilizzare un tipo bool (booleano) per memorizzare false e true ma posso lo stesso usare un tipo byte che contiene un valore numerico, tanto devo metterci dentro solo 0 o 1, anche se sotto mentite spoglie (false e true, appunto).

Nei confronti, come gli if, una condizione è vera quando ha un valore diverso da 0, altrimenti è falsa. Quindi
fare

if (stato == true)
if (stato == 1)
if (stato)

è perfettamente equivalente. L'ultima, anzi, è una forma contratta dato che avendo stato appunto un valore di 0 o 1, può solo essere o falso o vero.

Anche per la lettura del pin ho usato le stesse abbreviazioni.
Ho precisato che uso la logica del LOW per premuto, per cui se l'utente preme un pulsante, significa che viene letto LOW.
Il test "lungo" sarebbe quindi:

if (digitalWrite(pin) == LOW)

Ora, in Arduino LOW vale 0 e HIGH vale 1.
Quindi il precedente test è equivalente a:

if (digitalWrite(pin) == 0)

Ma riprendendo quanto detto prima, lo 0 vale sempre false nei test.
Se però io inverto il risultato di un false ottengo che il test è vero quando è falso, giusto?
Quindi

if (!digitalRead(pin))

significa appunto

se (inverti(digitalRead(pin))

digitalRead(pin) è falso quando il pin restituisce LOW, cioè 0.
Invertendo 0 ottengo 1, per cui l'if esterno diventa vero quando il pin è inverti(0).

Scusa, forse ho frainteso io la domanda ... forse non avevi capito proprio cosa intendesse Leo con quell' IF ?

Non ti preoccupare infatti io non avevo capito proprio l'espressione usata da Leo in questo caso...

if (stato == true)
if (stato == 1)
if (stato)
è perfettamente equivalente.

Grazie Leo per le apprezzabili delucidazioni...e anche a "gpb01 " ...ora è tutto chiaro ...voi siete i maestri e noi piano piano cerchiamo d'imparare...
E' evidente che chi sa masticare il C come voi usate delle scorciatoie per scrivere il codice...io purtroppo mi attengo alle formule più note e quelle che vedo già un pò diverse dalle altre per me sono "errori di forma" e invece sono chicche di saggezza...grazie a tutti...ora provo a concludere lo sketch che avevo postato all'inizio in modo tale da poterlo condividere con chi ne può avere bisogno...

Sicuramente Guglielmo sa masticare il C visto quel che ha fatto in tutti questi anni, io di sicuro no perché programmo in C da 2 anni e mezzo. :sweat_smile:
Solitamente non uso queste contrazioni quando scrivo codice per principianti proprio per evitare di incasinare ulteriormente l'utente che già di problemi ne ha di suo :wink:
Però stamani andavo di fretta ed ho scritto contratto per far prima. Manca anche la definizione di costante "const" messa davanti alla variabile del pin, così come ho usato un "byte" al posto di un "bool" perché avevo in mente una cosa e poi ne ho fatta un'altra :stuck_out_tongue_closed_eyes:

leo72:
Sicuramente Guglielmo sa masticare il C visto quel che ha fatto in tutti questi anni, io di sicuro no perché programmo in C da 2 anni e mezzo. :sweat_smile:
...

Leo non fare il modesto ... che sappiamo bene di cosa sei capace ... :slight_smile: :slight_smile: :slight_smile:

Guglielmo

Come promesso carico il codice e spiego il funzionamento...

Chiaramente lo sketch inizialmente è stato preso dagli esempi di futura elettronica da dove ho acquistato la simpatica shield 6IN/6OUT/6Analog e l'ho manipolato:

La scheda è in continuo ascolto sulla seriale e in base ai caratteri ricevuti compie delle azioni,ad esempio:

se digito sulla serile monitor dell'IDE arduino 1.03 :

"O1" arduino accende il relè n.1 sulla scheda ma bensì relativo al pin7
"O2" arduino accende il relè n.2 sulla scheda ma bensì relativo al pin8 ...etc..etc fino al relè n.6

"I1" arduino controlla lo stato dell'ingresso fisico n.1 sulla scheda ma bensì relativo al pin 2 digitale
"I2" arduino controlla lo stato dell'ingresso fisico n.2 sulla scheda ma bensì relativo al pin 3 digitale

Io ho cercato di implementare grazie all'aiuto di Leo e gpb01 un'altra funzione ovvero :

"J1" arduino accende il rele n.1 sulla scheda per 2 secondi e poi la spegne
"J2" arduino accende il rele n.2 sulla scheda per 2 secondi e poi la spegne etc..etc..

Ora ho incontrato un problema...ovvero se invio due comandi consecutivi tipo "J1 J2" il sistema non risponde in multitasking bensì accende prima il Relè 1,dopo due secondi spegne il Relè 1,
poi accende il relè 2,dopo due secondi spegne il Relè 2...

Io volevo usare millis() proprio per evitare i ritardi nel codice...come posso fare ???
Il problema potrebbe essere legato ai Switch-Case creati ??

Grazie se avete ancora la pazienza di seguirmi...vi ringrazio già in anticipo...

Mi ero dimenticato di caricare il codice...eccolo

// Variables will change:
int in=0;             
int out=0;
int an=0;
int inByte=0;

// the follow variables is a long because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long interval = 1000;           // interval at which to blink (milliseconds)

void setup() {
  
    // start serial port at 9600 bps:
  Serial.begin(9600);


  // set the digital pin as output:
  for (int out=8; out<=13; out++){
    pinMode(out, OUTPUT);      
  }
}

void loop()
{
  // here is where you'd put code that needs to be running all the time.

  // check to see if it's time to blink the LED; that is, if the 
  // difference between the current time and last time you blinked 
  // the LED is bigger than the interval at which you want to 
  // blink the LED.
 
  if (Serial.available() > 0) {
    protocollo();
  }
  
     
}


void protocollo()
{
  inByte = Serial.read();
    switch (inByte) 
    {
        case 79: //O  out
          
          
          Serial.println("Out number? (1 to 6)");   // send an initial string
            while (Serial.available() <= 0) 
            {
              delay(300);
            }
            
            out = Serial.read()-48;
            Serial.println("ricevuto ");
            Serial.print(out);
            if (out>=1&&out<=6)
            {
              out=out+7;
              if (!digitalRead(out))
                digitalWrite(out, HIGH);
              else
                digitalWrite(out, LOW);
                
              Serial.print("Out ");
              Serial.print(out-7);
              Serial.print(" = ");
              Serial.println(digitalRead(out));
            }
          
          break;
          
        case 73: //I
          //input
          
          Serial.println("In number? (1 to 6)");   // send an initial string
       
            while (Serial.available() <= 0) 
            {
              delay(300);
            }
            
            in = Serial.read()-48;
            if (in>=1&&in<=6){
            in=in+1;
            
            Serial.print("In ");
            Serial.print(in-1);
            Serial.print(" = ");
            Serial.println(digitalRead(in));
            }
          break;
          
          case 65: //A
          //analog
          
          Serial.println("Analog number? (1 to 6)");   // send an initial string
       
            while (Serial.available() <= 0) 
            {
              delay(300);
            }
            
            an = Serial.read()-48;
            if (an>=1&&an<=6){
            an=an-1;
            

            Serial.print("Analog ");
            Serial.print(an+1);
            Serial.print(" = ");
            Serial.println(analogRead(an));
            }
          break;

          
        //default: 
          // if nothing else matches, do the default
          // default is optional
     
    } 
 
   
}

Forse l'errore stà che dovrei specificare un millis per ogni uscita,quindi unsigned long myMillis1,unsigned long myMillis2...etc..etc spero di averci azzeccato...

io avrei fatto un array di tempi e un solo millis() di controllo nel loop.

Siccome il millis restituisce i millesimi che a te non servono toglierei le ultime tre cifre (/1000) ottenendo i secondi e preleverei solo l'ultima parte int (potresti farlo anche con i byte raggiungeresti un massimo di 4 minuti circa), poi a ogni ciclo loop confronto tutto l'array con il millis trascorsi, sempre /1000

Supponendo di avere 10 pin con funzione impulso regolabile a piacere:

salvare 10 uns long costa 4 byte10 ... 40 byte ti permette un impulso da 1 sec a 49 giorni (sprecato)
salvare 10 uns int costa 2 byte
10 ...20 byte ti permette un impulso da 1 sec a 18 ore circa (sprecato)
salvare 10 byte costa 1 byte*10 ...10 byte ti permette un impulso da 1 sec a 4 min circa (ottimizzato)

non ho fatto i conti precisi ma +/- siamo li

ciao

L'idea di un array la appoggio, un pò meno quella di usare i valori in secondi. Alla fine il risparmio è relativo in uno sketch nel complesso piccolo. Inoltre si aggrava di codice il programma per cui ad un risparmio di Ram ci devi aggiungere un consumo maggiore di Flash.

pietro78:
...
Ora ho incontrato un problema...ovvero se invio due comandi consecutivi tipo "J1 J2" il sistema non risponde in multitasking bensì accende prima il Relè 1,dopo due secondi spegne il Relè 1,
poi accende il relè 2,dopo due secondi spegne il Relè 2...
...

Pietro,
Arduino NON possiede un sistema operativo che gestisce i vari task o i vari processi ... su Arduino TUTTO è demandato al tuo codice quindi, se vuoi fare cose che sembrano funzionare in "multitask" ... le devi gestire tu. :wink:

Come ti è stato ben suggerito, costruisciti una serie di variabili (esempio, come ti ha detto pablos, un array) che usi per memorizzare le varie tempistiche, sapere quando hai attivato un determinato pin, calcolare quanto tempo è passato e, al momento giusto disattivarlo.

In pratica il tuo loop() deve sempre girare (senza mai fermarsi in pause o attese) effettuando più cose ...

... leggere la seriale e vedere se ci sono dei nuovi comandi
... eventualmente creare una coda di comandi ricevuti
... processare gli eventuali comandi in coda
... gestire una serie di "timer virtuali" (ovvero delle variabili in cui memorizzi tu il tempo) legati ai vari eventi
... attivare e disattivare i vari pin (o eseguire altre funzioni) in funzione dei comandi e dei tempi
... ecc. ecc.

In realtà è più complicato a spiegarsi che a farsi ... se ti metti li con pazienza e implementi un pezzetto per volta, vedrai che non avrai problemi :wink:

Guglielmo

P.S. : Altrimenti ... http://www.leonardomiliani.com/2012/leos-un-semplice-so-per-arduino/ ... che ti aiuta proprio in queste cose :wink:

gpb01:
P.S. : Altrimenti ... http://www.leonardomiliani.com/2012/leos-un-semplice-so-per-arduino/ ... che ti aiuta proprio in queste cose :wink:

Grazie per la segnalazione. :wink:
Il leOS è un piccolo scheduler che può eseguire piccoli compiti (task) ad intervalli regolari ed in totale trasparenza per il codice utente, eseguendo questi task in background. Quindi una volta lanciato, l'utente si può scordare del task che ha schedulato.

Leggevo ieri sera i post e naturalmente mi è venuta una soluzione, volevo usare Arduino IDE dopo tanto tempo che non lo uso per scrivere qualcosa passo passo, ma non ho ancora tempo, vedremo se riesco o se la cosa cade nel dimenticatoi.

Quando si crea un protocollo software ci si deve chiedere se questo deve essere generalizzato o specifico, quelli specifici sono più efficienti e semplici da implementare.

Se inviamo i dati in pacchetto, es >02552550025500< invio 8 byte + 2 10 byte totali. > rappresenta l'inizio degli 8 byte, dopo questo carattere/codice posso cominciare a coservare i dati in un array di almeno 8 byte, oppure 16, 24, ecc.

Il carattere < indica che lo stream ricevuto è terminato ed è completo e non devo scriverlo nell'array.

La posizione di ogni elemento già ci indica quale relè devo comandare, cioè quale pin, vero non proprio direttamente ma possiamo anche creare un altro array di 8 byte contenente gli pin, tanto l'indice di entrambe gli array è comune.

Già questo è un primo passo, il seguente è scrivere una funzione da dare in pasto a leOS o altro scheduler, se lo scheduler permettesse interrogazione circa il completamento di un task si potrebbe chiedere nel loop() miotask è stato processato, es scheduler.isTaskTermined(miotask) se ritorna true, posso cominciare nuovamente a leggere dalla seriale per vedere se ci sono altri comandi.

Questo già fa capire che lo scheduler non serve, perchè prima che tutti i rele siano stati messi nello stato specificato io non posso ricevere altri byte. Invece lo scheduler può tornare utile quando voglio prendere dei dati dalla seriale metterli nel buffer darli in pasto ad uno scheduler che li processa ogni millesimo di secondo e io tra un millesimo e l'altro eseguo codice nel loop che svolge altre funzioni come ad esempio l'intercettazione dei pulsanti premuti, ma non mi aspetto più dati dalla seriale.

Però lo scheduler di leo può tornare utile per il fatto che oltre ad eseguire codice ogni millesimo di secondo, permette di specificare il timeout almeno credo, ma forse allora è più indicato secTimer sempre di leo.

Ciao.