29/06/17 12:18
TheDarkJuster
Buongiorno a tutti, mi sto cimentando nella scritturar di un termostato con GSM. L'hardware è abbastanza semplice: 1 arduino, 1 shield gsm, qualche termometro e il necessario per gestire un buon relè.
Visto che volevo sperimentare con FreeRTOS ho deciso di usare il porting per arduino. Una di scelta di cui non mi pento, anche se mi ha dato diversi grattacapi: in primis succedevano veri e propri disastri con l'allocazione dinamica della memoria! Ho quindi dovuto disabilitarla, abilitare l'uso di memoria preallocata per tutte le strutture del SO ed evitare in tutti i modi di allocare nuova memoria, visto che il risultato di tale allocazione è un terno al lotto........
Trovate la versione modificata da me di FreeRTOS qui: github.com/NeroReflex/… prestate particolarmente attenzione a questo file scritto da me: github.com/NeroReflex/ArduinoRTOS/blob/master/src/… che è una raccolta di macro.
Ho poi importato FreeRTOS nel mio progetto arduino, che si compone. per il momento, di 2 task:
Il primo è quello che legge la temperatura attraverso un termometro e decide cosa farne:
E il thread di debug, che riceve le informazioni di debug dal task principale e le manda via seriale al pc:
Il task di debug ha priorità 1, il task principale 3 (la massima) significa che il task d debug può eseguirso SOLO mentre il task principale è in attesa di essere risvegliato (attraverso la syscall vTaskDelayUntil()).
Ora... Tutto ciò, funziona in maniera impeccabile. Non riscontro alcun problema.
Per capire il motivo della mia domandaa devo dirvi che il task che gestisce il modulo gsm è un task a se, che ho già preparato (almeno per quanto riguarda la struttura):
In questo codice ho disabilitato tutti i riferimenti alla classe SIM900, quindi non sarà posizionato sullo stack alcun elemento, mantenendo molto piccole le risorse utilizzate dal singolo task (per fare delle prove).
Il main è così, notate il commento posto proprio per evitare l'esecuzione dell'ultimo task:
Fin qui tutto bene. Una analisi dello schema di esecuzione rivela che:
vTaskStartScheduler() fa eseguire il processo principale perchè ha priorità maggiore, e nessun altro processo potrà eseguirsi finchè questo non si metterà in pausa. Quindi inizializza tutto ciò di cui ha bisogno al suo funzionamento e si mette in pausa appena entrato nel for.
Viene eseguito il processo secondario (di debug) che inizializza la seriale, stampa che il debug su schermo è iniziato e poco altro, visto che la coda è ancora vuota!
Viene risvegliato il processo principale, esegue una misura e la inserisce nella coda delle misure di debug. Il processo non può essere interrotto perché ha la priorità maggiore e nessun altro processo ha la stessa priorità, quindi la prima interruzione la farà al prossimo tentativo di lettura della temperatura (il for riparte).
A questo punto parte il processo di debug. Ha molto tempo per esegursi (circa 5 secondi) in questo lasso di tempo può SOLO trovare una misura sulla coda, stamparla a video e ciclare poi senza fare nulla, finchè il controllo non ritornerà al primo processo.
Il risultato è questo:
E fin qui tutto perfetto! Sono molto soddisfatto del risultato..... MA appena decommento la linea di creazione del task per il GSM succede un DISASTRO!
E qui si blocca TUTTO (presumibilmente)! Vedo il led del TX seriale che si accende, ma sul mio schermo non appare nient'altro.
Inoltre il led rimane acceso anche quando la temperatura supera 31° C. ma tutto ciò non dovrebbe assolutamente succedere!
Qualcuno sarebbe così gentile da darmi una mano a risolvere questo mistero?
Visto che volevo sperimentare con FreeRTOS ho deciso di usare il porting per arduino. Una di scelta di cui non mi pento, anche se mi ha dato diversi grattacapi: in primis succedevano veri e propri disastri con l'allocazione dinamica della memoria! Ho quindi dovuto disabilitarla, abilitare l'uso di memoria preallocata per tutte le strutture del SO ed evitare in tutti i modi di allocare nuova memoria, visto che il risultato di tale allocazione è un terno al lotto........
Trovate la versione modificata da me di FreeRTOS qui: github.com/NeroReflex/… prestate particolarmente attenzione a questo file scritto da me: github.com/NeroReflex/ArduinoRTOS/blob/master/src/… che è una raccolta di macro.
Ho poi importato FreeRTOS nel mio progetto arduino, che si compone. per il momento, di 2 task:
Il primo è quello che legge la temperatura attraverso un termometro e decide cosa farne:
#define pdMSTOTICKS( xTimeInMs ) ( (xTimeInMs / portTICK_PERIOD_MS) + 1 ) #ifdef SERIAL_DBG static struct BoilerRegulatorDebugInfo debugInfo; aImportStaticQueue(temperatureDebug); #endif aImportStaticSemaphore(temperaturePoll); // --------------------------------------------------- // -------------- Static variables ------------------- // --------------------------------------------------- static Logic logic; // boiler management logic static float oneWire_Temp = 127.5f; // last read temperature static bool boilerOn = false; // the boiler status TickType_t xLastWakeTime_BoilerRegulator; // used for the final delay static uint8_t deviceCount = 0; // the number of ds18x20 devices static bool validMeasure = false; // is the last measure valid? extern struct StatusPoll lastMeasure; // --------------------------------------------------- // ------------- Most important Task ----------------- // --------------------------------------------------- void TaskBoilerRegulator(void *pvParameters __attribute__((unused))) { #ifdef SERIAL_DBG logic.setThresholdTemperature(DBG_TEMPERATURE); logic.setHysteresis(DBG_HYSTERESIS); #endif // Set the heater pin pinMode(BOILER_DIGITAL_PIN, OUTPUT); digitalWrite(BOILER_DIGITAL_PIN, LOW); // setup thermometers OneWire ow(ONEWIRE_DIGITAL_PIN); DallasTemperature sensors(&ow); // This one is used for the good delay xLastWakeTime_BoilerRegulator = xTaskGetTickCount(); for (;;) { // A few seconds delay to let other tasks run vTaskDelayUntil(&xLastWakeTime_BoilerRegulator, pdMSTOTICKS( 5000 )); sensors.begin(); // IC Default 9 bit. If you have troubles consider upping it 12. Ups the delay giving the IC more time to process the temperature measurement sensors.setResolution(DS18X20_PRECISION_BITS); // Get the number of devices deviceCount = sensors.getDeviceCount(); // Read the temperature from the OneWire bus (if any device is present) if (deviceCount) { // Send the command to get temperatures sensors.requestTemperatures(); float temp = 127.5f; // get the lowest temperature from the group of ds18b20 for (int i = 0; i < deviceCount; i++) { // read the temperature float currentTemp = sensors.getTempCByIndex(i); // skip useless values! if (currentTemp < -60.0f) continue; else if (currentTemp > +50.0f) continue; // try updating the best temperature with the smallest one if (currentTemp < temp) { temp = currentTemp; validMeasure = true; } } // Update the current temperature if (validMeasure) oneWire_Temp = temp; validMeasure = false; // Update logic status logic.notifyTemperature(oneWire_Temp); // Edit pin value only if needed if ((bool)logic.heat() != boilerOn) { // Update the logic boilerOn = (logic.heat() == 0x01) ? true : false; // New mode for the boiler digitalWrite(BOILER_DIGITAL_PIN, (boilerOn != false) ? HIGH : LOW); } } // update the temperature for the GSM Shield task if (xSemaphoreTake(aGetStaticSemaphoreName(temperaturePoll), 3)) { lastMeasure.boilerOn = boilerOn; lastMeasure.temperatureValue = oneWire_Temp; } #ifdef SERIAL_DBG // Build the debug info structure debugInfo.count = (uint8_t)deviceCount; debugInfo.value = oneWire_Temp; debugInfo.enabled = boilerOn; // Attempt to send debug informations to the debug task xQueueSend(aGetStaticQueueName(temperatureDebug), (const void *)&debugInfo, 8); #endif } }
E il thread di debug, che riceve le informazioni di debug dal task principale e le manda via seriale al pc:
#define pdMSTOTICKS( xTimeInMs ) ( (xTimeInMs / portTICK_PERIOD_MS) + 1 ) #ifdef SERIAL_DBG aImportStaticQueue(temperatureDebug) void TaskDebug(void *pvParameters __attribute__((unused))) { // Setup the hardware serial port Serial.begin(115200); // Wait for the serial port to be ready while (!Serial); // Hello, my dear :) Serial.println("========================================"); Serial.println("Starting Debug (over serial 115200 baud)"); Serial.println("========================================"); for (;;) { // Get boiler regulator debug info struct BoilerRegulatorDebugInfo boilerRegulatorInfo; if(xQueueReceive(aGetStaticQueueName(temperatureDebug), (void*)&boilerRegulatorInfo, 3)) { Serial.println(); Serial.println("========================================"); if (boilerRegulatorInfo.count > 0) { Serial.print("OneWire thermometers: "); Serial.println(boilerRegulatorInfo.count); Serial.print("OneWire temperature: "); Serial.print(boilerRegulatorInfo.value); Serial.println("° C"); } else { Serial.println("No thermometers"); } if (boilerRegulatorInfo.enabled) Serial.println("Boiler ON"); else Serial.println("Boiler OFF"); Serial.println("========================================"); } } } #endif
Il task di debug ha priorità 1, il task principale 3 (la massima) significa che il task d debug può eseguirso SOLO mentre il task principale è in attesa di essere risvegliato (attraverso la syscall vTaskDelayUntil()).
Ora... Tutto ciò, funziona in maniera impeccabile. Non riscontro alcun problema.
Per capire il motivo della mia domandaa devo dirvi che il task che gestisce il modulo gsm è un task a se, che ho già preparato (almeno per quanto riguarda la struttura):
#define pdMSTOTICKS( xTimeInMs ) ( (xTimeInMs / portTICK_PERIOD_MS) + 1 ) aImportStaticSemaphore(temperaturePoll); // --------------------------------------------------- // -------------- Static variables ------------------- // --------------------------------------------------- struct StatusPoll lastMeasure; TickType_t xLastWakeTime_MobileManagement; void TaskMobileManagement(void *pvParameters) { //SIM900 sim900; xLastWakeTime_MobileManagement = xTaskGetTickCount(); for (;;) { // A few seconds delay to let other tasks run vTaskDelayUntil(&xLastWakeTime_MobileManagement, pdMSTOTICKS( 2000 )); /*SIM900DateTime currentTime; if (sim900.getTime(currentTime)) { }*/ } }
In questo codice ho disabilitato tutti i riferimenti alla classe SIM900, quindi non sarà posizionato sullo stack alcun elemento, mantenendo molto piccole le risorse utilizzate dal singolo task (per fare delle prove).
Il main è così, notate il commento posto proprio per evitare l'esecuzione dell'ultimo task:
// -------------------------------------------------- // --------------------- Queues --------------------- // -------------------------------------------------- aDefineStaticQueue(temperatureDebug, sizeof(struct BoilerRegulatorDebugInfo), 1); // -------------------------------------------------- // ------------------- Semaphores ------------------- // -------------------------------------------------- aDefineStaticSemaphore(temperaturePoll); // -------------------------------------------------- // --------------------- Tasks ---------------------- // -------------------------------------------------- aDefineStaticTask(MobileCommunication, (configMINIMAL_STACK_SIZE + 25)); aDefineStaticTask(BoilerRegulator, (configMINIMAL_STACK_SIZE + 125)); #ifdef SERIAL_DBG aDefineStaticTask(Debug, (configMINIMAL_STACK_SIZE + 25)); #endif // -------------------------------------------------- // -------------------- FreeRTOS -------------------- // -------------------------------------------------- void setup() { // Initialize Semaphores aInitStaticMutexSemaphore(temperaturePoll); // Initialize Queue aInitStaticQueue(temperatureDebug, sizeof(struct BoilerRegulatorDebugInfo), 1); // Prepare tasks aCreateTask(BoilerRegulator, TaskBoilerRegulator, NULL, (configMAX_PRIORITIES - 1)); //aCreateTask(MobileCommunication, TaskBoilerRegulator, NULL, (configMAX_PRIORITIES - 2)); #ifdef SERIAL_DBG aCreateTask(Debug, TaskDebug, NULL, (configMAX_PRIORITIES - 3)); #endif // Start the real time scheduler. vTaskStartScheduler(); // Probably we've failed trying to initialise heap for the scheduler. Let someone know. vApplicationMallocFailedHook(); }
Fin qui tutto bene. Una analisi dello schema di esecuzione rivela che:
vTaskStartScheduler() fa eseguire il processo principale perchè ha priorità maggiore, e nessun altro processo potrà eseguirsi finchè questo non si metterà in pausa. Quindi inizializza tutto ciò di cui ha bisogno al suo funzionamento e si mette in pausa appena entrato nel for.
Viene eseguito il processo secondario (di debug) che inizializza la seriale, stampa che il debug su schermo è iniziato e poco altro, visto che la coda è ancora vuota!
Viene risvegliato il processo principale, esegue una misura e la inserisce nella coda delle misure di debug. Il processo non può essere interrotto perché ha la priorità maggiore e nessun altro processo ha la stessa priorità, quindi la prima interruzione la farà al prossimo tentativo di lettura della temperatura (il for riparte).
A questo punto parte il processo di debug. Ha molto tempo per esegursi (circa 5 secondi) in questo lasso di tempo può SOLO trovare una misura sulla coda, stamparla a video e ciclare poi senza fare nulla, finchè il controllo non ritornerà al primo processo.
Il risultato è questo:
======================================== Starting Debug (over serial 115200 baud) ======================================== ======================================== OneWire thermometers: 2 OneWire temperature: 27.00° C Boiler ON <<<====== Ho impostato per motivi di debug la temperatura minima a 30.0° C ecco perchè è accesa ======================================== ======================================== OneWire thermometers: 2 OneWire temperature: 27.00° C Boiler ON ======================================== ======================================== OneWire thermometers: 2 OneWire temperature: 27.00° C Boiler ON ======================================== ======================================== OneWire thermometers: 2 OneWire temperature: 27.00° C Boiler ON ======================================== ======================================== OneWire thermometers: 2 OneWire temperature: 27.00° C Boiler ON ======================================== Poi metto un dito sui termometri e l temperatura si alza ======================================== OneWire thermometers: 2 OneWire temperature: 27.50° C Boiler ON ======================================== ======================================== OneWire thermometers: 2 OneWire temperature: 27.75° C Boiler ON ======================================== ======================================== OneWire thermometers: 2 OneWire temperature: 28.05° C Boiler ON ======================================== ECCETERA.... ======================================== OneWire thermometers: 2 OneWire temperature: 31.14° C Boiler OFF <<<=== L'isteresi è di un grado, quindi a un grado sopra il 30 si spegne ========================================
E fin qui tutto perfetto! Sono molto soddisfatto del risultato..... MA appena decommento la linea di creazione del task per il GSM succede un DISASTRO!
======================================== Starting Debug (over serial 115200 baud) ======================================== ======================================== No thermometers Boiler OFF ======================================== ======================================== No thermometers Boiler OFF ======================================== ======================================== OneWire thermometers: 2 OneWire temperature: 26.19° C Boiler ON ======================================== ======================================== OneWire thermometers: 2 OneWire temperature: 26.19° C Boiler ON ======================================== ======================================== OneWire thermometers: 2 OneWire temperature: 26.19° C Boiler ON ========================================
E qui si blocca TUTTO (presumibilmente)! Vedo il led del TX seriale che si accende, ma sul mio schermo non appare nient'altro.
Inoltre il led rimane acceso anche quando la temperatura supera 31° C. ma tutto ciò non dovrebbe assolutamente succedere!
Qualcuno sarebbe così gentile da darmi una mano a risolvere questo mistero?
aaa