Je suis sur un projet et je souhaite optimiser mon code de réception des données.
Le shéma de "circulation" des données est le suivant:
[Stalker 2.1+Xbee] ---------> [ARduinoUno+ShieldXBee+ShieldEthernet+TMP102+LCD] ---------> conversion des données reçues ---------> publication et affichage sur le LCD ---------> publication et affichage sur page internet
J'émet les données à partir du Stalker avec un envoi Serial.print formaté de la façon suivante:
YYYY/MM/DD;HH:MM:SS;TT.TT;
(données avec un séparateur de type ";")
Avec:
YYYY = année
MM = Mois
DD = Date
HH = Heure
MM = Minutes
SS= Secondes
TT.TT = Température en deg Celsius (par exemple 22.05)
Mon problème est que je ne m'y connais pas assez en programmation C pour faire ce dont j'ai envi.
Je cherche donc quelqu'un qui pourrait m'aider à optimiser mon code en fonction de mes besoins.
Je ne pense pas qu'il y ait besoin de changer la partie "Emission" des données provenant du [Stalker 2.1+Xbee], sauf si ça aide pour la partie réception.
Mon problème se pose avec la partie réception.
La difficulté est que la carte de réception doit à la fois faire de la "surveillance" de réception de données, et dans le même temps faire de la "publication" de données sur le LCD et Ethernet
Et là au niveau des timmings ça coince.
Je n'arrive pas à en même temps "surveiller" les données qui pourraient arriver du [Stalker 2.1+Xbee] et à publier ces mêmes données sur le LCD et Ethernet.
ça coince car le LCD n'affiche pas les données correctement, parfois il scintille.
Pour ce qui est de la partie Ethernet, la page met du temps avant de s'actualiser.
J'ai utilisé la fonction Switch/case mais je sens bien que ce n'est pas optimisé car j'ai des résultats bizarres de temps en temps...
Je poste mon code ici si une âme charitable de dévellopeur pouvait passer
/* Data logger using Stalker V2.1 Logs temperature every 15 sec to a file DATALOG.CSV
[Stalker+XBEE]--->[Uno+Ethernet+Xbee+TMP102+LDR+LCD]
v 0.01
john_lenfr
*/
//// INCLUDES FILES ////
#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/power.h>
#include <Wire.h>
#include <DS3231.h>
#include <Fat16.h>
#include <Fat16util.h>
//// END INCLUDES FILES ////
//The following code is taken from sleep.h as Arduino Software v22 (avrgcc) in w32 does not have the latest sleep.h file
#define sleep_bod_disable() \
{ \
uint8_t tempreg; \
__asm__ __volatile__("in %[tempreg], %[mcucr]" "\n\t" \
"ori %[tempreg], %[bods_bodse]" "\n\t" \
"out %[mcucr], %[tempreg]" "\n\t" \
"andi %[tempreg], %[not_bodse]" "\n\t" \
"out %[mcucr], %[tempreg]" \
: [tempreg] "=&d" (tempreg) \
: [mcucr] "I" _SFR_IO_ADDR(MCUCR), \
[bods_bodse] "i" (_BV(BODS) | _BV(BODSE)), \
[not_bodse] "i" (~_BV(BODSE))); \
}
//// VARIABLES ////
DS3231 RTC; //Create RTC object for DS3231 RTC come Temp Sensor
static uint8_t prevSecond=0;
static DateTime interruptTime;
SdCard card;
Fat16 file;
int batterystatus1; // variable used to check battery status
int batterystatus2; // variable used to check battery status
// Variable used to set the date into the Dallas DS3231 real time clock
// !! Writing any non-existent time-data may interfere with normal operation of the RTC and probably will crash your sketch!!
// !! Take care of week-day also !!
// DateTime dt(2011, 12, 30, 13, 39, 0, 5); // syntax: year, month, date, hour, min, sec and week-day(starts from 0 and goes to 6)
// use this variable with the fonction RTC.adjust(dt); in void setup()
//// END VARIABLES ////
// store error strings in flash to save RAM
#define error(s) error_P(PSTR(s))
void error_P(const char* str) {
PgmPrint("error: ");
SerialPrintln_P(str);
if (card.errorCode) {
PgmPrint("SD error: ");
//Serial.println(card.errorCode, HEX);
}
while(1);
}
//// SET UP ////
void setup ()
{
// XBEE related module
digitalWrite(5, HIGH); // power ON xbee module: can be placed where you want in the sketch
//digitalWrite(5, LOW); // power OFF xbee module: can be placed where you want in the sketch
delay(5000); // wait for the start or stop of xbee to be stabilized
// Initialize INT0 pin of the timer chip for accepting interrupts ie awake when we want it (to save battery life)
// !!! DON'T FORGET TO WIRE PD2 TO /INT WITH SOLDIER ON BOTTOM OF THE BOARD TO ENABLE IT!!!
PORTD |= 0x04;
DDRD &=~ 0x04;
pinMode(4, INPUT); // extern power for SD Card reader
pinMode(5, OUTPUT); // digital pin 5 control xbee power only if EN and PD5 are wire on bottom of the board
pinMode(6, INPUT); // digital pin 6 give the status of the charging: LOW=CHARGING; HIGH=NOT CHARGING
pinMode(7, INPUT); // digital pin 7 give the status of the battery: LOW=BATTERY CHARGED; HIGH=NOT CHARGED or DAMAGE
pinMode(8, OUTPUT); // digital pin 8 control led "D8_LED" that can be used for any pupose. This led is on the board.
// start serial communications and setup the baud transfert to 9600 or 57600
Wire.begin();
Serial.begin(9600); // use to normal communications
//Serial.begin(57600); //use to set date and time
RTC.begin();
attachInterrupt(0, INT0_ISR, LOW); //Only LOW level interrupt can wake up from PWR_DOWN
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
// Date Time setup (if needed)
// Used to set the date into the Dallas DS3231 real time clock
// Don't forget to set time in the DateTime dt declaration on top of the sketch!
// Comment it when time is setup
// RTC.adjust(dt); //Adjust date-time as defined 'dt' above in the DateTime dt declaration
//Enable Interrupt
//RTC.enableInterrupts(EveryMinute); //interrupt at EverySecond, EveryMinute, EveryHour
// or this
DateTime start = RTC.now();
interruptTime = DateTime(start.get() + 15); //Add 15 secs to start time
// check battery status
batterystatus1 = digitalRead(6); // DIGITAL PIN 6 is "CHARGER READ"
if (batterystatus1 == LOW){
Serial.println("battery is charging");
}
else
{
Serial.println("battery is not charging");
}
batterystatus2 = digitalRead(7); // DIGITAL PIN 7 is "OK READ"
if (batterystatus2 == LOW){
Serial.println("battery is fully charged");
}
else
{
Serial.println("battery charging incomplete");
}
//read and print battery "analog reading"
Serial.println(analogRead(7)); // ANALOG PIN 7 is "BATTERY READ"
// End checking battery status
// XBEE related module
//digitalWrite(5, HIGH); // power ON xbee module: can be placed where you want in the sketch
digitalWrite(5, LOW); // power OFF xbee module: can be placed where you want in the sketch
delay(1000); // wait for the start or stop of xbee to be stabilized
}
//// END SET UP ////
//// START MAIN ////
void loop ()
{
////////////////////// START : Application or data logging code//////////////////////////////////
RTC.convertTemperature(); //convert current temperature into registers
float temp = RTC.getTemperature(); //Read temperature sensor value
DateTime now = RTC.now(); //get the current date-time
if((now.second()) != prevSecond )
{
//print only when there is a change
// starting sending paquet
Serial.print(now.year(), DEC);
Serial.print('/');
Serial.print(now.month(), DEC);
Serial.print('/');
Serial.print(now.date(), DEC);
Serial.print(';');
Serial.print(now.hour(), DEC);
Serial.print(':');
Serial.print(now.minute(), DEC);
Serial.print(':');
Serial.print(now.second(), DEC);
Serial.print(";");
Serial.print(temp);
Serial.print(";");
Serial.println();
// Ending sending paquet
}
prevSecond = now.second();
//|||||||||||||||||||Write to Disk||||||||||||||||||||||||||||||||||
// initialize the SD card
if (!card.init()) error("card.init");
// initialize a FAT16 volume
if (!Fat16::init(&card)) error("Fat16::init");
char name[] = "DATALOG.CSV";
// clear write error
file.writeError = false;
// O_CREAT - create the file if it does not exist
// O_APPEND - seek to the end of the file prior to each write
// O_WRITE - open for write
if (!file.open(name, O_CREAT | O_APPEND | O_WRITE))
error("error opening file");
digitalWrite(8, HIGH); //power ON led on the board when printing on SD Card
//start printing on SD Card
file.print(now.year(), DEC);
file.print('/');
file.print(now.month(), DEC);
file.print('/');
file.print(now.date(), DEC);
file.print(',');
file.print(now.hour(), DEC);
file.print(':');
file.print(now.minute(), DEC);
file.print(':');
file.print(now.second(), DEC);
file.print(',');
file.println(temp);
//end printing on SD Card
digitalWrite(8, LOW); //power OFF led on the board when ENDING printing on SD Card
if (!file.close())
error("error closing file");
//|||||||||||||||||||Write to Disk||||||||||||||||||||||||||||||||||
RTC.clearINTStatus(); //This function call is a must to bring /INT pin HIGH after an interrupt.
RTC.enableInterrupts(interruptTime.hour(),interruptTime.minute(),interruptTime.second()); // set the interrupt at (h,m,s)
attachInterrupt(0, INT0_ISR, LOW); //Enable INT0 interrupt (as ISR disables interrupt). This strategy is required to handle LEVEL triggered interrupt
////////////////////////END : Application code ////////////////////////////////
//\/\/\/\/\/\/\/\/\/\/\/\/Sleep Mode and Power Down routines\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
// Turn in sleeping mode and turn off a maximum of devices on the board.
// Shut down all peripherals like ADC before sleeping. Refer Atmega328 manual for more info
cli();
sleep_enable(); // Set sleep enable bit
sleep_bod_disable(); // Disable brown out detection during sleep. Saves more power
sei();
//Serial.println("\nSleeping");
delay(10); //This delay is required to allow print to complete
//power down XBEE module to END of transmition
digitalWrite(5, LOW); // power OFF xbee module
delay(1000); // wait for the complete stop of xbee
power_all_disable(); //This shuts down ADC, TWI, SPI, Timers and USART
sleep_cpu(); // Sleep the CPU as per the mode set earlier(power down)
// Awake from sleep
sleep_disable(); // Wakes up sleep and clears enable bit. Before this ISR would have executed
power_all_enable(); //This enables ADC, TWI, SPI, Timers and USART
delay(10); //This delay is required to allow CPU to stabilize
//wake up XBEE module to transmit
digitalWrite(5, HIGH); // power ON xbee module
delay(1000); // wait for the complete start of xbee
//Serial.println("Awake from sleep");
//\/\/\/\/\/\/\/\/\/\/\/\/Sleep Mode and Power Saver routines\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
}
//// END MAIN ////
//// UTILITIES AND PROGS ////
//Interrupt service routine for external interrupt on INT0 pin conntected to DS3231 /INT
void INT0_ISR()
{
//Keep this as short as possible. Possibly avoid using function calls
detachInterrupt(0);
interruptTime = DateTime(interruptTime.get() + 15); //decide the time for next interrupt, configure next interrupt
}
//// END UTILITIES AND PROGS ////
/*Temperature Datalogger
Published on Ethernet and LCD screen
[Stalker+XBEE]--->[Uno+Ethernet+Xbee+TMP102+LDR+LCD]
v 0.01
john_lenfr
*/
//// INCLUDES LIBRARIES ////
#include <SPI.h> // for the Ethernet Shield
#include <Ethernet.h> // for the Ethernet Shield
#include <Wire.h> // for the TMP102 temperature
#include <LiquidCrystal.h> // for the LCD Screen
#include <Time.h> // for refresh
//// END INCLUDES LIBRARIES ////
//// VARIABLES ////
// global variables to control main loop()
int valeur=1;
int t;
// variables for ethernet shield
byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0x9C, 0x0E };
byte ip[] = { 192, 168, 1, 11 };
Server server(80);
// variables for incoming data from the xbee stalker v2.1 board (place outdoor)
int i=0;
int j=0;
int k=0;
int l=0;
char incoming; // a variable to read incoming serial data from xbee
char inData[80] = {"YYYY/MM/DD;HH:MM:SS;TT.TT;"}; // buffer for recording incoming data and then publish it on the web part
char stalkerDate[15] = {NULL};
char stalkerHour[15] = {NULL};
char stalkerTemp[15] = {NULL};
// variables for TMP102 temperature sensor (indoor)
int tmp102Address = 0x48;
byte res;
float celsius;
// variables for the LCD Screen these constants won't change. But you can change the size of
// your LCD using them:
const int numRows = 2;
const int numCols = 20;
int delais=0;
int len = strlen(inData); // for detection of refresh if needed or not
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(3, 14, 9, 8, 7, 6, 5);
// RS, RW, Enable, 4,5,6,7 (Refer to the back of your LCD for details)
//// SETUP ////
void setup()
{
Serial.begin(9600); // setup serial communication bauds
Wire.begin(); // start communicating with TMP102 temperature sensor
Ethernet.begin(mac, ip); // start the ethernet port
server.begin(); // start the server board shield
lcd.begin(numRows, numCols);// set up the LCD's number of rows and columns
lcd.clear(); // clear lcd screen
lcd.setCursor(0,0); // set the cursor on top left hand corner of the lcd screen
lcd.print("***** ARDUINO *****");
lcd.setCursor(0,2); // left, second line
lcd.print("Uno Ethernet XBEE");
lcd.setCursor(0,3); // left, second line
lcd.print(" TMP102 LDR ");
delay(2000);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("*** TEMPERATURES ***");
lcd.setCursor(0,1);
lcd.print("Interieure: ");
lcd.print(celsius);
lcd.print(176, BYTE);
lcd.print("C");
lcd.setCursor(0,2);
lcd.print("Exterieure: ");
lcd.print(stalkerTemp);
lcd.print(176, BYTE);
lcd.print("C");
lcd.setCursor(0,3);
lcd.print(stalkerDate);
lcd.setCursor(11,3);
lcd.print(len);
len = strlen(inData);
}
//// END SETUP ////
//// START MAIN ////
void loop()
{
t = now(); // get local unix time
// change LCD display every five seconds
switch(valeur)
{
case 1: // reading tmp102 internal temperature
tmp102();
valeur=2;
break;
case 2: // reading Stalker v2 external data communicating with XBEE radio module
xbee();
valeur=3;
break;
case 3: // cut received data into separate strings
if(second(t) % 5 == 0) {// refresh
stringTostring();
len = strlen(inData);
}
valeur=4;
break;
case 4: // publish results on webserver
ether();
valeur=5;
break;
case 5: // publish results on lcd screen
if(second(t) % 5 == 0) {// refresh LCD display
liquidcrystaldisplay();
}
valeur=1;
break;
default: //problem in the program
problem();
break;
}
}
//// END MAIN ////
Le code n'est quand même pas très optimisé mais il fonctionne comme je veux.
J'ai donc remplacé la partie réception XBEE:
void xbee(){
/******** START BEE COMMUNICATIONS ********/
// Listen for XBee
while (Serial.available() > 0){
// read the oldest byte in the serial buffer:
incoming = Serial.read();
Serial.print(incoming,BYTE);
inData[i] = incoming;
i++;
}
// End of communications with xbee
/******** END XBEE COMMUNICATIONS ********/
}
PAR:
void xbee(){
/******** START BEE COMMUNICATIONS ********/
while (Serial.available() > 0){
// read the oldest byte in the serial buffer:
inData[i] = Serial.read();
delay(10); // data can properly recorded in the tab inData
i++;
}
i=0;
/******** END XBEE COMMUNICATIONS ********/
}
Et j'ai changé le timer de refresh du LCD. Au lieu d'utiliser un refresh utilisant les secondes, j'ai utilisé les microsecondes, ce qui me permet d'utiliser la fonction lcd.clear() plus facilement.
Ici le LCD s'actualise au bout de 5 secondes, mais on peut faire varier la durée avec la variable "const unsigned long DISPLAY_INTERVAL = 5000".
J'ai donc changé le code pour le lcd :
// Time interval [ms] for display updates:
const unsigned long DISPLAY_INTERVAL = 5000; // in ms 200ms equals approximately 5 Hz
static unsigned long lastTime=0; // in ms
unsigned long time=millis(); // in ms
case 5: // publish results on lcd screen
time=millis();
if (time-lastTime>DISPLAY_INTERVAL) // if at least DISPLAY_INTERVAL ms have passed
{
liquidcrystaldisplay(); // update display
lastTime=time; // reset timer
}
valeur=1;
break;
Avis aux amateurs d'optimisation de code
Il me reste encore à optimiser ma fonction stringTostring qui découpe la chaine reçue par le XBEE en 3 chaines différentes et les place dans 3 tableaux de char différents: stalkerDate/stalkerHour/stalkerTemp
Mis en boîte comme ça, ça a de la gueule ! J'ai parcouru ton code (en travers j'avoue) et un truc me saute au yeux. Au niveau des échanges entre les différentes cartes, tu utilises des chaînes de caractères. Tu pourrais définir un format de message qui ne contiendrait que des octets sans aucune mise en forme (pas de '/', ':', etc...). Cette mise en forme ne se ferait que sur l'Arduino Uno chargée de l'affichage des données et autres opérations. Et au passage économiser pas mal de mémoire.
Par exemple pour le message du type YYYY/MM/DD;HH:MM:SS;TT.TT; tu enverrais :
.------------------.----------------.----------------.-----------------.------------------.-------------------.-----------------------------.------------------------------.
| Année (2 octets) | Mois (1 octet) | Jour (1 octet) | Heure (1 octet) | Minute (1 octet) | Seconde (1 octet) | Partie entière T° (1 octet) | Partie décimale T° (1 octet) |
'------------------'----------------'----------------'-----------------'------------------'-------------------'-----------------------------'------------------------------'
Ainsi ta trame ne fait que 9 octets au lieu des 26 octets de ta chaîne de caractères. Voici une piste : Type-Length-Value. Par contre c'est vrai que ça fait plus de travail lorsque tu veux afficher les données...
Tout ça est à creuser et il y a certainement moyen de faire bien mieux, l'objectif étant juste de te donner quelques pistes...
Cool la boiboite.
Comme le dit Sesech il y a moyen d'envoyé des trames plus courtes, les chaine de caractère sont de gros consommateurs mémoire et calcul pour leurs traitements.
C'est ce que je précisais dans ses posts:
(pub pour le projet )
Entre () j'ai pas étudier tout le code mais je vois un while(1) en fin de fonction (gestion d'erreur ?), c'est risqué non ?
Tu peux également éviter d'envoyer trop régulièrement la date et l'heure et faire un synchro de temps en temps.
Pour le contrôle de mon chauffage centrale, mon arduino est complètement autonome pour l'instant, pas de server ntp, de ds1307, etc. et l'horloge est conservée de façon interessante (librairie time). Je modifie mon code, je branche mon pc, j'envoie mon nouveau code et je resynchronise l'horloge par la même occasion. Plusieurs semaines, voire un mois complet, sans resynchroniser l'horloge et elle reste à un niveau trés correcte (affichage à la minute).
Sur un mois, le décalage doit être d'environ 10 à 20 secondes.
Une synchro journalière est amplement suffisante.