98a6009624f37faa090d414c7bd7cf2b02f7a67f
[bertos.git] / boards / at91sam7x-ek / examples / sd_fat / main.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2010 Develer S.r.l. (http://www.develer.com/)
30  *
31  * -->
32  *
33  * \author Daniele Basile <asterix@develer.com>
34  *
35  * \brief Simple application that implement a log device, running on the AT91SAM7X-EK board.
36  *
37  * The main scope of this application is to show you how use the SD memory card and read and write
38  * settings and logs. Thanks the ini parser module we can read settings, stored on memory card,
39  * and change the application behaviour.
40  * Generally the application work in this way:
41  * - Try to access to SD memory card, if it is present we read the inifile
42  * - If we have read correctly from SD, we compute the value that we want log
43  *   (temperature, pressure and supplay voltage)
44  * - If we have enable (from ini file) to log on the file we write the log on it.
45  * - If we have enable (from ini file) to log on the serial we write the log on it.
46  * - wait the sample_time (from ini file)
47  * - contine from beginning.
48  *
49  * Here we put the ini file that this example uses. To use it copy
50  * the folling configutation line into the file called sd_fat.ini (or see INI_FILE_NAME
51  * define if you want chande the name).
52  * \code
53  *
54  *   # Bertos SD fat project example
55  *   #
56  *   # Basic configurarion
57  *   #
58  *
59  *   [log]
60  *   # Name of log file
61  *   name = test.log
62  *   # Enable the logging on serial device (enable = 1, disable = 0)
63  *   log_on_serial = 1
64  *   # Enable logging on sd file (enable = 1, disable = 0)
65  *   log_on_file = 1
66  *   # Period between two log in millisecond
67  *   sample_time = 1000
68  *
69  *   [serial_log]
70  *   # Select serial port where log
71  *   port = 0
72  *   # Serial port baudrate
73  *   baud = 115200
74  *
75  *   [log_format]
76  *   # Default text to insert on each log line
77  *   line_header = BeRTOS Log:
78  *   # Use this char to separate each log field
79  *   field_sep = ;
80  *
81  *   [temperature]
82  *   unit_label = C
83  *
84  *   [pressure]
85  *   unit_label = hPa
86  *
87  *   [voltage]
88  *   unit_label = V
89  *
90  * \endcode
91  */
92
93 #include <cfg/debug.h>
94
95 // Define logging setting (for cfg/log.h module).
96 #define LOG_LEVEL         3
97 #define LOG_VERBOSITY     0
98 #include <cfg/log.h>
99 #include <cfg/macros.h>
100
101 #include <cpu/irq.h>
102
103 #include <drv/timer.h>
104 #include <drv/sd.h>
105 #include <drv/ser.h>
106 #include <drv/tmp123.h>
107 #include <drv/spi_dma_at91.h>
108 #include <drv/mpxx6115a.h>
109 #include <drv/adc.h>
110
111 #include <fs/fat.h>
112
113 #include <mware/ini_reader.h>
114
115 #include <verstag.h>
116 #include <buildrev.h>
117
118 #include <string.h>
119 #include <stdio.h>
120 #include <stdlib.h>
121
122 /*
123  * Setting structure define
124  */
125 typedef struct INISetting
126 {
127         // INI Log section
128         char name[80];              ///< Name of log file
129         bool log_on_serial;         ///< Enable the logging on serial device
130         bool log_on_file;           ///< Enable logging on sd file
131         mtime_t sample_time;        ///< Period between two log (millisecond)
132
133         //INI serial log section
134         int port;                   ///< Select serial port where log
135         int baud;                   ///< Serial port baudrate
136
137         // INI log format section
138         char line_header[80];       ///< Default text to insert on each log line
139         char field_sep[10];         ///< Use this char to separate each log field
140
141         // INI temperature section
142         char temp_unit_label[10];   ///< Default label for measure unit
143
144         // INI pressure section
145         char press_unit_label[10];  ///< Default label for measure unit
146
147         // INI voltage section
148         char voltage_unit_label[10];  ///< Default label for measure unit
149
150
151 } INISetting;
152
153 /*
154  * Setting and various defines.
155  */
156 #define INI_FILE_NAME     "sd_fat.ini"   ///< Default ini file name on SD card.
157
158 // INI Log section
159 #define LOG_SECTION        "log"           ///< Log section
160 #define LOG_NAME_KEY       "name"          ///< Name of log file
161 #define LOG_ON_SERIAL      "log_on_serial" ///< Enable the logging on serial device
162 #define LOG_ON_FILE        "log_on_file"   ///< Enable logging on sd file
163 #define LOG_SAMPLE_TIME    "sample_time"   ///< Period between two log
164
165 //INI serial log section
166 #define SERIAL_LOG         "serial_log"    ///< Serial log section
167 #define SERIAL_PORT        "port"          ///< Select serial port where log
168 #define SERIAL_BAUD        "baud"          ///< Serial port baudrate
169
170 // INI log format section
171 #define LOG_FORMAT_SEC     "log_format"    ///< log format section
172 #define FORMAT_LINE_HEADER "line_header"   ///< Default text to insert on each log line
173 #define FORMAT_FIELD_SEP   "field_sep"     ///< Use this char to separate each log field
174
175 // INI temperature section
176 #define TEMPERATURE        "temperature"   ///< temperarute section
177 #define TEMP_UNIT_LABEL    "unit_label"    ///< Default label for measure unit
178
179 // INI pressure section
180 #define PRESSURE           "pressure"      ///< pressure section
181 #define PRESS_UNIT_LABEL   "unit_label"    ///< Default label for measure unit
182
183 // INI voltsge section
184 #define VOLTAGE            "voltage"      ///< voltage section
185 #define VOLTAGE_UNIT_LABEL "unit_label"   ///< Default label for measure unit
186
187 /**
188  * Voltage reference in volts for ADC.
189  * \note This value should be complied to jumper J13 on AT91SAM7X-EK board, and
190  * the supply voltade for sensor.
191  * See schamatics for more info.
192  */
193 #define ADC_VOLTAGE_REF             3300
194 /**
195  * Channel where the sensor we connect it (AD6)
196  * See schamatics for more info.
197  */
198 #define PRESSURE_SENSOR_CH          6
199
200 /**
201  * ADC channle to read voltage supplay
202  * \note we connect board power supplay (+3.3V) to AD3 port with wire.
203  */
204 #define SUPPLAY_VOLTAGE_CH          3
205 #define ADC_SUPPLAY_VOLTAGE         3300
206
207 /*
208  * Static definition for application devices.
209  */
210 static SpiDmaAt91 spi_dma;
211 static Serial temp_sensor_bus;
212 static Serial log_port;
213
214 static void init(void)
215 {
216         IRQ_ENABLE;
217         kdbg_init();
218
219         timer_init();
220         LOG_INFO("TIMER init..ok\n");
221
222         /*
223          * Init temperature sensor device.
224          * - init the temperature driver
225          * - init the spi communication channel
226          */
227         tmp123_init();
228         // Init SPI connected to sensor temperature
229         spimaster_init(&temp_sensor_bus, SER_SPI1);
230         ser_setbaudrate(&temp_sensor_bus, 1000000L);
231         LOG_INFO("TMP123 sensor init..ok\n");
232
233         // Init SPI bus to communicate to SD card
234         spi_dma_init(&spi_dma);
235         spi_dma_setclock(20000000L);
236         LOG_INFO("SD SPI init..ok\n");
237
238         adc_init();
239         LOG_INFO("ADC init..ok\n");
240
241 }
242
243 int main(void)
244 {
245         // SD fat filesystem context
246         FATFS fs;
247
248         // Context files that we would manage
249         FatFile ini_file;
250         FatFile log_file;
251
252         init();
253
254         LOG_INFO("SD fat example project %s: %d times\n", VERS_HOST, VERS_BUILD);
255
256         while (1)
257         {
258                 bool sd_ok = true;
259                 FRESULT result;
260
261                 // Setting info
262                 INISetting ini_set;
263                 memset(&ini_set, 0, sizeof(ini_set));
264
265                 sd_ok = sd_init(&sd, &spi_dma.fd, 0);
266
267                 if (sd_ok)
268                 {
269                         LOG_INFO("Mount FAT filesystem.\n");
270                         if ((result = f_mount(0, &fs)) != FR_OK)
271                         {
272                                 LOG_ERR("Mounting FAT volumes error[%d]\n", result);
273                                 sd_ok = false;
274                         }
275
276                         LOG_INFO("Read setting from ini file: %s\n", INI_FILE_NAME);
277                         if (sd_ok && ((result = fatfile_open(&ini_file, INI_FILE_NAME, FA_READ)) != FR_OK))
278                         {
279                                 LOG_ERR("Could not open ini file: %s error[%d,]\n", INI_FILE_NAME, result);
280                                 sd_ok = false;
281                         }
282
283                         if (sd_ok)
284                         {
285                                 /*
286                                  * If sd is ok, we read all setting from INI file.
287                                  * NOTE: if one ini key or section was not found into INI
288                                  * file, the iniparser return the defaul value.
289                                  */
290                                 ini_getString(&ini_file.fd, LOG_SECTION, LOG_NAME_KEY, "default.log", ini_set.name, sizeof(ini_set.name));
291                                 LOG_INFO("Log file name [%s]\n", ini_set.name);
292
293                                 char tmp[25];
294                                 ini_getString(&ini_file.fd, LOG_SECTION, LOG_ON_SERIAL,   "1", tmp, sizeof(tmp));
295                                 ini_set.log_on_serial = atoi(tmp);
296                                 LOG_INFO("Log serial [%d]\n", ini_set.log_on_serial);
297                                 ini_getString(&ini_file.fd, LOG_SECTION, LOG_ON_FILE,     "1", tmp, sizeof(tmp));
298                                 ini_set.log_on_file = atoi(tmp);
299                                 LOG_INFO("Log sd [%d]\n", ini_set.log_on_file);
300                                 ini_getString(&ini_file.fd, LOG_SECTION, LOG_SAMPLE_TIME, "500", tmp, sizeof(tmp));
301                                 ini_set.sample_time = atoi(tmp);
302                                 LOG_INFO("Sample time [%ld]\n", ini_set.sample_time);
303
304                                 ini_getString(&ini_file.fd, SERIAL_LOG, SERIAL_PORT, "0", tmp, sizeof(tmp));
305                                 ini_set.port = atoi(tmp);
306                                 LOG_INFO("Serial port [%d]\n", ini_set.port);
307                                 ini_getString(&ini_file.fd, SERIAL_LOG, SERIAL_BAUD, "115200", tmp, sizeof(tmp));
308                                 ini_set.baud = atoi(tmp);
309                                 LOG_INFO("Serial buad [%d]\n", ini_set.baud);
310
311                                 ini_getString(&ini_file.fd, LOG_FORMAT_SEC, FORMAT_LINE_HEADER, "BeRTOS: ", ini_set.line_header, sizeof(ini_set.line_header));
312                                 LOG_INFO("Serial line header[%s]\n", ini_set.line_header);
313
314                                 ini_getString(&ini_file.fd, LOG_FORMAT_SEC, FORMAT_FIELD_SEP, ",", ini_set.field_sep, sizeof(ini_set.field_sep));
315                                 LOG_INFO("Serial char sep[%s]\n", ini_set.field_sep);
316
317                                 ini_getString(&ini_file.fd, TEMPERATURE, TEMP_UNIT_LABEL, "C", ini_set.temp_unit_label, sizeof(ini_set.temp_unit_label));
318                                 LOG_INFO("Temp unit label[%s]\n", ini_set.temp_unit_label);
319
320                                 ini_getString(&ini_file.fd, PRESSURE, PRESS_UNIT_LABEL, "hPa", ini_set.press_unit_label, sizeof(ini_set.press_unit_label));
321                                 LOG_INFO("Press unit label[%s]\n", ini_set.press_unit_label);
322
323                                 ini_getString(&ini_file.fd, VOLTAGE, VOLTAGE_UNIT_LABEL, "V", ini_set.voltage_unit_label, sizeof(ini_set.voltage_unit_label));
324                                 LOG_INFO("Press unit label[%s]\n", ini_set.voltage_unit_label);
325
326                         }
327                 }
328
329                 if (ini_set.log_on_serial)
330                 {
331                         // Init serial log port
332                         ser_init(&log_port, ini_set.port);
333                         ser_setbaudrate(&log_port, ini_set.baud);
334                         LOG_INFO("SERIAL init..port[%d] buad[%d]\n", ini_set.port, ini_set.baud);
335                 }
336
337                 char log_string[160];
338                 memset(log_string, 0, sizeof(log_string));
339
340                 // Supplay voltage
341                 uint16_t vdd = ADC_RANGECONV(adc_read(SUPPLAY_VOLTAGE_CH), 0, ADC_SUPPLAY_VOLTAGE);
342
343                 // Read temperature
344                 int16_t tmp = tmp123_read(&temp_sensor_bus.fd);
345
346                 // Read pressure
347                 uint16_t vout = ADC_RANGECONV(adc_read(PRESSURE_SENSOR_CH), 0, vdd);
348                 int16_t press = mpxx6115a_press(vout,  vdd);
349
350                 /*
351                  * Format string whit read data
352                  * line_header + temp + temp_unit_label + field_sep + press + press_unit_label + field_sep + vdd + voltage_unit_label
353                  */
354                 int wr_len = sprintf(log_string, "%s%d.%01d%s%s%d%s%s%d.%d%s\r\n", ini_set.line_header,
355                                                                                                                                 tmp / 10, ABS(tmp % 10),
356                                                                                                                                 ini_set.temp_unit_label,
357                                                                                                                                 ini_set.field_sep,
358                                                                                                                                 press,
359                                                                                                                                 ini_set.press_unit_label,
360                                                                                                                                 ini_set.field_sep,
361                                                                                                                                 vdd / 1000, ABS(vdd % 1000),
362                                                                                                                                 ini_set.voltage_unit_label);
363
364
365                 /*
366                  * if SD is ok, try to open log file and write our data and, only
367                  * if by configuration we have enable the log on file
368                  */
369                 if (sd_ok && ini_set.log_on_file)
370                 {
371                         // Open log file and do not overwrite the previous log file
372                         result = fatfile_open(&log_file, ini_set.name,  FA_OPEN_EXISTING | FA_WRITE);
373
374                         // If the log file there isn't we create the new one
375                         if (result == FR_NO_FILE)
376                         {
377                                 result = fatfile_open(&log_file, ini_set.name,  FA_CREATE_NEW | FA_WRITE);
378                                 LOG_INFO("Create the log file: %s\n", ini_set.name);
379                         }
380
381                         if ( result == FR_OK)
382                         {
383                                 LOG_INFO("Opened log file '%s' size %ld\n", ini_set.name, log_file.fat_file.fsize);
384
385                                 // To append data we should go to end of file before to start to write
386                                 kfile_seek(&log_file.fd, 0, KSM_SEEK_END);
387
388                                 int len = kfile_write(&log_file.fd, log_string, wr_len);
389
390                                 // Flush data and close the files.
391                                 kfile_flush(&log_file.fd);
392                                 kfile_close(&log_file.fd);
393
394                                 // Unmount always to prevent accidental sd remove.
395                                 f_mount(0, NULL);
396                                 LOG_INFO("Wrote [%d]\n", len);
397                         }
398                         else
399                         {
400                                 LOG_ERR("Unable to open file: '%s' error[%d]\n", ini_set.name, result);
401                         }
402                 }
403
404                 // If by configuration we have enable the log on serial, we log it
405                 if (ini_set.log_on_serial)
406                         kfile_write(&log_port.fd, log_string, wr_len);
407
408                 timer_delay(ini_set.sample_time);
409         }
410
411         return 0;
412 }
413