Buonasera a tutti. Ho creato un prototipo con Arduino uno + GSM Shield. Il prototipo esegue nel LOOP la lettura di eventuali messaggi in arrivo e calcola la temperatura della stanza. La procedura di calcolo della temperatura mi permette di impostare la temperatura desiderata nella stanza, la confronta con la rilevata e decide se accendere o spegnere il riscaldamento. La regolazione della temperatura la faccio mediante trimmer. Ho la necessità di leggere immediatamente il nuovo valore del trimmer senza aspettare il ciclo che dura qualche secondo. Non posso usare interrupt in quanto funzionano solo sulle porte digitali e inoltre il pin 2 e 3 è già occupato dalla gsm shield. mi chiedevo se c'era modo di fare due thread in modo da leggere il trimmer con il primo e calcolare la temperatura con il secondo. Così facendo l' aggiornamento del valore del trimmer dovrebbe essere simultaneo con il calcolo della temperatura. Stavo valutando di collegare un arduino nano che mi faccia parte dell'operazione, per esempio il calcolo della temperatura.
Prima di tutto, nella sezione in lingua Inglese si può scrivere SOLO in Inglese ... quindi, per favore, la prossima volta presta più attenzione, poi, essendo questo il tuo primo post, ti chiedo cortesemente di presentarti QUI (spiegando bene quali conoscenze hai di elettronica e di programmazione) e di leggere con attenzione il REGOLAMENTO ... Grazie.
Guglielmo
NON sei su un PC dove c'è un sistema operativo, ci sono più "core", c'è un multitasking e una gestione multithread ... qui sei su una piccola MCU dove devi fare tutto tu da codice.
Si trovano delle librerie che aiutano a fare degli scheduler, ma in ogni caso quando gira una cosa NON ne gira un altra. Esempi sono la "looper" e la "leOS2" ...
Guglielmo
Ciao, come @gpb01 ti ha fatto notare, Arduino è un Microcontroller Single-Core che non può eseguire multitasking/multithreading.
Hai due possibili "soluzioni":
-
Usare un multitasking "fittizio", ovvero sia creare dei "loop 2,3,4..." che si alternino tra loro durante i richiami dei delay in maniera talmente rapida da dare l'impressione di lavorare in parallelo (in realtà si sta solo passando da una funzione ad un'altra in un tempo rapidissimo).
Sul forum (usa la funzione cerca!) ci sono moltissimi thread sull'argomento (specialmente perchè molte persone sopravvalutano il potenziale di questa piattaforma, o lo interpretano male). -
rtOS, ovvero "Real Time Operative System"; si tratta di cambiamenti radicali che vengono apportati allo stile di scrittura poichè coinvolgono funzioni particolari che permettono una gestione ancora migliore dello "scheduler", cioè colui che si occupa di "smistare" le funzioni del tuo programma. E' come la prima soluzione, ma migliore.
@c0rsa1r: NON esiste una versione di "rtOS" per Arduino UNO ... esistono quelle librerie che ho indicato nel mio post ...
Ricorda che sei su una piccola MCU a 8 bit che ha solo 32KB di flash e 2KB di SRAM ... un po' pochini ... ![]()
Guglielmo
@gbp01 ti sei dimenticato del porting scheduler ufficiale! funziona su tutte le schede Arduino con Arm o Avr
State dando suggerimenti per alevare i sintomi ma non la causa.
Il problema lo vedo nel loop() che dura qualche secondo.
Per primo é strano che un ciclo duri cosí a lungo (eccetto un uso di delay())
per secondo non vedo la problematica se una temperatura e il valore di riferimento vengano letti non frequentemente.
stefano_8805 facci cedere lo sketch.
Ciao Uwe
vbextreme:
@gbp01 ti sei dimenticato del porting scheduler ufficiale! funziona su tutte le schede Arduino con Arm o Avr
Si, vero, in ogni caso è un'altro affare come leOS2, leOS, looper, ecc ... uno scheduler ... e non un RTOS multithread ![]()
Guglielmo
@gbp01 no! veramente il mio è l'unico vero scheduler tra quelli proposti!
Gli altri emulano lo scheduler, il mio ha un vero context switch che tocca punte di 11us per il cambio di task. Sto cercando di aggiornalo per renderlo ancora piu veloce.
Quindi con il mio hai un vero multitask! guardati gli esempi, penso troverai interessante il ServoWithoutTimer....
@gbp01, non volevo dirtelo ma lo scheduler è il kernel e un RTOS è un kernel ![]()
@VB ... so benissimo cosa è un kernel
... qui si parlava di capacità di multi-thread e multi-tasking ...
... non ho esaminato a fondo il tuo ... appena ho un po' d tempo ci do un'occhiata ...
Guglielmo
Buongiorno a tutti e grazie per gli spunti. Mi ero già informato su google su eventuali soluzioni da poter adottare. Ho intitolato il post impropriamente multithread in quanto ero già a conoscenza che arduino non è un multi core di conseguenza non è possibile gestire più processi contemporaneamente, motivo per cui ho ipotizzato di aggiungere un arduino nano per dividere le operazioni.
Il problema che ho è dovuto al fatto che quando "giro" il trimmer e quindi cambia la temperatura da raggiungere, l'aggiornamento del nuovo valore sul display ci mette qualche secondo a visualizzarsi, invece mi piacerebbe fosse immediato. Verificando con la funzione millis() i tempi noto che il ciclo di calcolo temperatura e lettura trimmer è un po' lento (qualche secondo). L'unico delay presente dura 1000ms che mi server per dare tempo al sensore di temperature di leggere 100 volte la temperatura per poi mediarla e aumentare la precisione. Se preferite vi posto lo sketch completo.
Facci vedere lo sketch.
Ciao Uwe
ecco qui lo sketch completo. Allego anche il file.
// includo le librerie
#include <GSM.h>
#include <EEPROMex.h>
#include <LiquidCrystal.h>
//Inizializzo i pin dell' LCD
LiquidCrystal lcd(4,5,17,16,15,14);
//Dichiaro le costanti dei PIN\
const int GateReleControlPin = 9;
const int RiscReleControlPin = 10;
const int LCDLightPin = 6;
const int LCDOnOffPin = 8;
const int RegTempManualPin = 11;
const int TempSensorPin = A4;
const int TempRegPin = A5;
//dichiaro le variabili globali
String Riscaldamento = "";
float temperature;
float TempMem,TempNew = 0;
char _bufferTempImp[5] = "";
char _bufferTempRil[5] = "";
char _bufferTempNew[5] = "";
//Stringa di memorizzazione SMS in ingresso
String message = "";
//Stringhe di memorizzazione parametri SMS
String commandID, deviceID, param;
//Array di memorizzazione del numero di telefono
char senderNumber[20];
// initialize the library instances
GSM gsmAccess;
GSM_SMS sms;
void setup()
{
//Inizializzo LCD Vcc PIN
pinMode(LCDLightPin, OUTPUT);
digitalWrite(LCDLightPin,HIGH);
lcd.begin(16,2);
//Inizializzo GSM Shield
GSMinitialization();
//inizializzo la seriale
Serial.begin(9600);
writeLCD("Inizializzazione","PIN",1000);
// inizilizzo i pin
pinMode(GateReleControlPin, OUTPUT);
pinMode(RiscReleControlPin, OUTPUT);
pinMode(LCDOnOffPin, INPUT);
pinMode(RegTempManualPin, INPUT);
digitalWrite(GateReleControlPin,HIGH);
digitalWrite(RiscReleControlPin,HIGH);
//Attivo confronto con AREF in modo da migliorare
analogReference(EXTERNAL);
//Leggo la temperatura memorizzata in EEPROM
TempMem = EEPROM.readDouble(0);
}
void loop()
{
//Verifico se ci sono messaggi da leggere
if (sms.available())
smsProcess();
//Calcolo temperatura
calcTemp();
//on off display alla pressione di un pulsante
if (digitalRead(LCDOnOffPin) == HIGH)
{
if (digitalRead(LCDLightPin) == HIGH)
digitalWrite(LCDLightPin,LOW);
else
digitalWrite(LCDLightPin,HIGH);
}
}
float calcTemp()
{
//inizializzo variabili
int val_Adc = 0;
temperature = 0;
//eseguo un ciclo
for(byte Ciclo = 0; Ciclo<100; Ciclo++)
{
//acquisisco il valore e lo sommo alla variabile
val_Adc += analogRead(TempSensorPin);
//questo ritardo serve per dare il tempo all’ ADC di eseguire correttamente la prossima acquisizione
delay(10);
}
//eseguo la media dei 100 valori letti
val_Adc /= 100;
//calcolo la temperatura in °C
temperature =(((val_Adc * 0.0032)-0.5)/0.01);
if (digitalRead(RegTempManualPin) == HIGH)
{
//Serial.println("Impostazione manuale temperatura.");
TempNew = ((analogRead(TempRegPin)*0.010)+15);
dtostrf(TempNew, 2, 1, _bufferTempNew);
TempNew = atof (_bufferTempNew);
if (TempNew != TempMem)
{
EEPROM.writeDouble(0,TempNew);
TempMem = EEPROM.readDouble(0);
}
}
else
{
Serial.println("Impostazione temperatura via SMS.");
TempMem = EEPROM.readDouble(0);
}
if ((temperature < TempMem) or (analogRead(TempRegPin)>1010))
{
Riscaldamento = "ON";
digitalWrite(RiscReleControlPin,LOW);
}
if ((temperature > TempMem) or (analogRead(TempRegPin)<10))
{
Riscaldamento = "OFF";
digitalWrite(RiscReleControlPin,HIGH);
}
//arrotondo i double per tenere un solo decimale
dtostrf(temperature, 2, 1, _bufferTempRil);
dtostrf(TempMem, 2, 1, _bufferTempImp);
writeLCD("T Ril Imp Ctr" ," "+String(_bufferTempRil)+" "+String(_bufferTempImp)+" "+Riscaldamento,0);
return temperature;
}
void GSMinitialization()
{
writeLCD("Inizializzazione","GSM",0);
//Stato connessione
boolean notConnected = true;
//Avvio connessione GSM
while(notConnected)
{
if(gsmAccess.begin() == GSM_READY)
{
notConnected = false;
}
else
{
writeLCD("GSM non","connesso",2000);
Serial.println("GSM non connesso");
delay(1000);
}
}
writeLCD("GSM","Inizializzato",0);
Serial.println("GSM Inizializzato");
}
void smsProcess()
{
char c;
Serial.println("Message received from:");
// Get remote number
sms.remoteNumber(senderNumber, 20);
Serial.println(senderNumber);
// An example of message disposal
// Any messages starting with # should be discarded
// usually anonymous messages starts with #
if(sms.peek()=='#')
{
writeLCD("Discarded SMS","",2000);
//Serial.println("Discarded SMS");
sms.flush();
}
// Read message bytes and print them
while(c = sms.read())
message += c;
// cancella message ricevuto
sms.flush();
writeLCD("MESSAGE DELETED","",2000);
Serial.println("MESSAGE DELETED");
//Se ci sono messaggi inizio ad elaborarli
if(message != "")
{
//modifico il messaggio e lo scrivo tutto minuscolo
message.toLowerCase();
writeLCD(message,"",2000);
Serial.println(message);
int i=0;
int j=0;
//cerco il carattere di fine device
while(message.charAt(i) != '*') i++;
//cerco carattere di fine comando
while(message.charAt(j) != '+') j++;
deviceID = message.substring(0,i );
commandID = message.substring(i+1,j);
param = message.substring(j+1,message.length());
execCommand(deviceID,commandID,param);
// pulisco il messaggio processato
message = "";
}
}
void execCommand(String device,String command, String param)
{
commandID.trim();
deviceID.trim();
String rispostaSMS = "";
//controllo il device da gestire
if(deviceID == "cancello")
{
Serial.println(deviceID);
//controllo che comando eseguire
if(commandID == "apri")
{
//sendFeedbackSMS(senderNumber, deviceID, "in apertura"); // send the feedback to the sender
digitalWrite(GateReleControlPin,LOW);
delay(100);
digitalWrite(GateReleControlPin,HIGH);
writeLCD("Cancello in","apertura",2000);
Serial.println("cancello in apertura");
}
}
// controllo il device da interrogare
if(deviceID == "termometro")
{
Serial.println(deviceID);
//controllo il comando da eseguire
if(commandID == "read")
{
Serial.println("calcolo temperatura");
rispostaSMS = "Temperatura rilevata "+ String(calcTemp())+" temperatura impostata "+ EEPROM.readDouble(0);
sendFeedbackSMS(senderNumber, rispostaSMS ,"");
}
if(commandID == "set")
{
Serial.println("imposta temperatura");
EEPROM.writeDouble(0, param.toFloat());
Serial.print(EEPROM.readDouble(0));
TempMem = param.toFloat();
}
}
}
// this function, given a phone number and two strings representing the device and
// its state send a feedback text message to the phone number
void sendFeedbackSMS(char remoteNum[], String devString, String devState) {
String txtMsg = devString + " " + devState;
Serial.println(txtMsg);
Serial.println(remoteNum);
sms.beginSMS(remoteNum);
sms.print(txtMsg);
sms.endSMS();
}
void writeLCD(String riga1,String riga2,int ritardo)
{
lcd.setCursor(0,0);
lcd.print(" ");
lcd.setCursor(0,1);
lcd.print(" ");
lcd.setCursor(0,0);
lcd.print(riga1);
lcd.setCursor(0,1);
lcd.print(riga2);
if (ritardo > 0)
{
delay(ritardo);
lcd.setCursor(0,0);
lcd.print(" ");
lcd.setCursor(0,1);
lcd.print(" ");
}
}
SMS_3.ino (6.82 KB)
Premetto che è OT, ma non è certo vero che per fare il multithreading e/o il multitasking serva più di un core :).
Fine OT.
SukkoPera:
non è certo vero che per fare il multithreading e/o il multitasking serva più di un core :).
Sicuramente NO ... dato che nei bei tempi passati si faceva pure con il i80386 ...
... però se ce li hai riesci ad avere un certo parallelismo che, ovviamente, in altro modo non è possibile ![]()
Guglielmo
bhe questo sarebbe un bel programmino da poter eseguire lo scheduler...
@stefano_8805 hai guardato il link che ho postato?
Ora lo guardo e provo ad applicarlo. Poi vi faccio sapere se ho risolto. Lo scopo fondamentale è simulare qualcosa che mi dia il risultato del multi task. Intanto grazie.
Ho provato ad usare lo scheduler, ma sicuramente sbaglio qualcosa in quanto esegue solo il loop2. Ho provato persino a togliere provvisoriamente una parte di codice per vedere se funzionava. Vi allego lo sketch nuovo. Leggendo in giro sembrerebbe funzioni solo su arduino due mentre io ho arduino uno.
Vi ringrazio fin da ora per le risposte.
// includo le librerie
#include <GSM.h>
#include <EEPROMex.h>
#include <LiquidCrystal.h>
#include <Scheduler.h>
//Inizializzo i pin dell' LCD
LiquidCrystal lcd(4,5,17,16,15,14);
//Dichiaro le costanti dei PIN\
const int GateReleControlPin = 9;
const int RiscReleControlPin = 10;
const int LCDLightPin = 6;
const int LCDOnOffPin = 8;
const int RegTempManualPin = 11;
const int TempSensorPin = A4;
const int TempRegPin = A5;
//dichiaro le variabili globali
String Riscaldamento = "";
float temperature;
float TempMem,TempNew = 0;
char _bufferTempImp[5] = "";
char _bufferTempRil[5] = "";
char _bufferTempNew[5] = "";
//Stringa di memorizzazione SMS in ingresso
String message = "";
//Stringhe di memorizzazione parametri SMS
String commandID, deviceID, param;
//Array di memorizzazione del numero di telefono
char senderNumber[20];
// initialize the library instances
GSM gsmAccess;
GSM_SMS sms;
void setup()
{
//Inizializzo LCD Vcc PIN
pinMode(LCDLightPin, OUTPUT);
digitalWrite(LCDLightPin,HIGH);
lcd.begin(16,2);
//inizializzo la seriale
Serial.begin(9600);
writeLCD("Inizializzazione","PIN",1000);
// inizilizzo i pin
pinMode(9, OUTPUT);
pinMode(RiscReleControlPin, OUTPUT);
pinMode(LCDOnOffPin, INPUT);
pinMode(RegTempManualPin, INPUT);
digitalWrite(9,HIGH);
digitalWrite(RiscReleControlPin,HIGH);
//Attivo confronto con AREF in modo da migliorare
analogReference(EXTERNAL);
//Leggo la temperatura memorizzata in EEPROM
TempMem = EEPROM.readDouble(0);
Scheduler.startLoop(loop2);
Scheduler.startLoop(loop3);
}
void loop()
{
//Calcolo temperatura
//calcTemp();
//on off display alla pressione di un pulsante
if (digitalRead(LCDOnOffPin) == HIGH)
{
if (digitalRead(LCDLightPin) == HIGH)
digitalWrite(LCDLightPin,LOW);
else
digitalWrite(LCDLightPin,HIGH);
}
delay(3000);
}
void loop3()
{
//inizializzo variabili
int val_Adc = 0;
temperature = 0;
//eseguo un ciclo
for(byte Ciclo = 0; Ciclo<100; Ciclo++)
{
//acquisisco il valore e lo sommo alla variabile
val_Adc += analogRead(TempSensorPin);
//questo ritardo serve per dare il tempo all’ ADC di eseguire correttamente la prossima acquisizione
delay(10);
}
//eseguo la media dei 100 valori letti
val_Adc /= 100;
//calcolo la temperatura in °C
temperature =(((val_Adc * 0.0032)-0.5)/0.01);
if ((temperature < TempMem) or (analogRead(TempRegPin)>1010))
{
Riscaldamento = "ON";
digitalWrite(RiscReleControlPin,LOW);
}
if ((temperature > TempMem) or (analogRead(TempRegPin)<10))
{
Riscaldamento = "OFF";
digitalWrite(RiscReleControlPin,HIGH);
}
//arrotondo i double per tenere un solo decimale
dtostrf(temperature, 2, 1, _bufferTempRil);
dtostrf(TempMem, 2, 1, _bufferTempImp);
writeLCD("T Ril Imp Ctr" ," "+String(_bufferTempRil)+" "+String(_bufferTempImp)+" "+Riscaldamento,0);
yield();
}
void writeLCD(String riga1,String riga2,int ritardo)
{
lcd.setCursor(0,0);
lcd.print(" ");
lcd.setCursor(0,1);
lcd.print(" ");
lcd.setCursor(0,0);
lcd.print(riga1);
lcd.setCursor(0,1);
lcd.print(riga2);
if (ritardo > 0)
{
delay(ritardo);
lcd.setCursor(0,0);
lcd.print(" ");
lcd.setCursor(0,1);
lcd.print(" ");
}
}
void loop2()
{
if (digitalRead(RegTempManualPin) == HIGH)
{
Serial.println("Impostazione manuale temperatura.");
TempNew = ((analogRead(TempRegPin)*0.010)+15);
dtostrf(TempNew, 2, 1, _bufferTempNew);
TempNew = atof (_bufferTempNew);
if (TempNew != TempMem)
{
EEPROM.writeDouble(0,TempNew);
TempMem = EEPROM.readDouble(0);
}
}
else
{
Serial.println("Impostazione temperatura via SMS.");
TempMem = EEPROM.readDouble(0);
}
delay(3000);
}
se hai scaricato lo scheduler dal mio repository funziona anche sulla Uno.
più tardi guardo il tuo codice.
prova a mettere un Serial.print nel setup e dentro ogni loop così vedi meglio cosa accade