5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
25 const char * g_logError
= nullptr;
30 #if defined(PCBTARANIS) || defined(PCBHORUS)
31 int getSwitchState(uint8_t swtch
) {
32 int value
= getValue(MIXSRC_FIRST_SWITCH
+ swtch
);
33 return (value
== 0) ? 0 : (value
< 0) ? -1 : +1;
36 #define GET_2POS_STATE(sw) (switchState(SW_ ## sw) ? -1 : 1)
37 #define GET_3POS_STATE(sw) (switchState(SW_ ## sw ## 0) ? -1 : (switchState(SW_ ## sw ## 2) ? 1 : 0))
42 memset(&g_oLogFile
, 0, sizeof(g_oLogFile
));
45 const char * logsOpen()
47 // Determine and set log file filename
49 char filename
[sizeof(LOGS_PATH
) + LEN_MODEL_NAME
+ 16]; // /LOGS/modelnamexxxxxx_2013-01-01.log
54 if (sdGetFreeSectors() == 0)
55 return STR_SDCARD_FULL
;
57 // check and create folder here
58 strcpy(filename
, STR_LOGS_PATH
);
59 const char * error
= sdCheckAndCreateDirectory(filename
);
64 filename
[sizeof(LOGS_PATH
) - 1] = '/';
65 memcpy(&filename
[sizeof(LOGS_PATH
)], g_model
.header
.name
, sizeof(g_model
.header
.name
));
66 filename
[sizeof(LOGS_PATH
) + LEN_MODEL_NAME
] = '\0';
68 uint8_t i
= sizeof(LOGS_PATH
) + LEN_MODEL_NAME
- 1;
70 while (i
> sizeof(LOGS_PATH
) - 1) {
71 if (!len
&& filename
[i
])
75 filename
[i
] = zchar2char(filename
[i
]);
84 uint8_t num
= g_eeGeneral
.currModel
+ 1;
89 strcpy(&filename
[sizeof(LOGS_PATH
)], STR_MODEL
);
90 filename
[sizeof(LOGS_PATH
) + PSIZE(TR_MODEL
)] = (char)((num
/ 10) + '0');
91 filename
[sizeof(LOGS_PATH
) + PSIZE(TR_MODEL
) + 1] = (char)((num
% 10) + '0');
92 len
= sizeof(LOGS_PATH
) + PSIZE(TR_MODEL
) + 2;
95 char * tmp
= &filename
[len
];
98 tmp
= strAppendDate(&filename
[len
]);
101 strcpy(tmp
, STR_LOGS_EXT
);
103 result
= f_open(&g_oLogFile
, filename
, FA_OPEN_ALWAYS
| FA_WRITE
| FA_OPEN_APPEND
);
104 if (result
!= FR_OK
) {
105 return SDCARD_ERROR(result
);
108 if (f_size(&g_oLogFile
) == 0) {
115 tmr10ms_t lastLogTime
= 0;
120 if (f_close(&g_oLogFile
) != FR_OK
) {
121 // close failed, forget file
122 g_oLogFile
.obj
.fs
= 0;
132 f_puts("Date,Time,", &g_oLogFile
);
134 f_puts("Time,", &g_oLogFile
);
138 char label
[TELEM_LABEL_LEN
+7];
139 for (int i
=0; i
<MAX_TELEMETRY_SENSORS
; i
++) {
140 if (isTelemetryFieldAvailable(i
)) {
141 TelemetrySensor
& sensor
= g_model
.telemetrySensors
[i
];
143 memset(label
, 0, sizeof(label
));
144 zchar2str(label
, sensor
.label
, TELEM_LABEL_LEN
);
145 uint8_t unit
= sensor
.unit
;
146 if (unit
== UNIT_CELLS
) unit
= UNIT_VOLTS
;
147 if (UNIT_RAW
< unit
&& unit
< UNIT_FIRST_VIRTUAL
) {
149 strncat(label
, STR_VTELEMUNIT
+1+3*unit
, 3);
153 f_puts(label
, &g_oLogFile
);
158 #if defined(PCBTARANIS) || defined(PCBHORUS)
159 for (uint8_t i
=1; i
<NUM_STICKS
+NUM_POTS
+NUM_SLIDERS
+1; i
++) {
160 const char * p
= STR_VSRCRAW
+ i
* STR_VSRCRAW
[0] + 2;
161 for (uint8_t j
=0; j
<STR_VSRCRAW
[0]-1; ++j
) {
163 f_putc(*p
, &g_oLogFile
);
166 f_putc(',', &g_oLogFile
);
169 for (uint8_t i
=0; i
<NUM_SWITCHES
; i
++) {
170 if (SWITCH_EXISTS(i
)) {
171 char s
[LEN_SWITCH_NAME
+ 2];
173 temp
= getSwitchName(s
, SWSRC_FIRST_SWITCH
+ i
* 3);
176 f_puts(s
, &g_oLogFile
);
179 f_puts("LSW,", &g_oLogFile
);
181 f_puts("Rud,Ele,Thr,Ail,P1,P2,P3,THR,RUD,ELE,3POS,AIL,GEA,TRN,", &g_oLogFile
);
184 f_puts("TxBat(V)\n", &g_oLogFile
);
187 uint32_t getLogicalSwitchesStates(uint8_t first
)
190 for (uint8_t i
=0; i
<32; i
++) {
191 result
|= (getSwitch(SWSRC_FIRST_LOGICAL_SWITCH
+first
+i
) << i
);
198 static const char * error_displayed
= nullptr;
204 if (isFunctionActive(FUNCTION_LOGS
) && logDelay
> 0) {
205 tmr10ms_t tmr10ms
= get_tmr10ms();
206 if (lastLogTime
== 0 || (tmr10ms_t
)(tmr10ms
- lastLogTime
) >= (tmr10ms_t
)logDelay
*10) {
207 lastLogTime
= tmr10ms
;
209 if (!g_oLogFile
.obj
.fs
) {
210 const char * result
= logsOpen();
212 if (result
!= error_displayed
) {
213 error_displayed
= result
;
214 POPUP_WARNING(result
);
222 static struct gtm utm
;
223 static gtime_t lastRtcTime
= 0;
224 if (g_rtcTime
!= lastRtcTime
) {
225 lastRtcTime
= g_rtcTime
;
228 f_printf(&g_oLogFile
, "%4d-%02d-%02d,%02d:%02d:%02d.%02d0,", utm
.tm_year
+TM_YEAR_BASE
, utm
.tm_mon
+1, utm
.tm_mday
, utm
.tm_hour
, utm
.tm_min
, utm
.tm_sec
, g_ms100
);
231 f_printf(&g_oLogFile
, "%d,", tmr10ms
);
234 for (int i
=0; i
<MAX_TELEMETRY_SENSORS
; i
++) {
235 if (isTelemetryFieldAvailable(i
)) {
236 TelemetrySensor
& sensor
= g_model
.telemetrySensors
[i
];
237 TelemetryItem
& telemetryItem
= telemetryItems
[i
];
239 if (sensor
.unit
== UNIT_GPS
) {
240 if (telemetryItem
.gps
.longitude
&& telemetryItem
.gps
.latitude
) {
241 div_t qr
= div((int)telemetryItem
.gps
.latitude
, 1000000);
242 if (telemetryItem
.gps
.latitude
< 0) f_printf(&g_oLogFile
, "-");
243 f_printf(&g_oLogFile
, "%d.%06d ", abs(qr
.quot
), abs(qr
.rem
));
244 qr
= div((int)telemetryItem
.gps
.longitude
, 1000000);
245 if (telemetryItem
.gps
.longitude
< 0) f_printf(&g_oLogFile
, "-");
246 f_printf(&g_oLogFile
, "%d.%06d,", abs(qr
.quot
), abs(qr
.rem
));
249 f_printf(&g_oLogFile
, ",");
252 else if (sensor
.unit
== UNIT_DATETIME
) {
253 f_printf(&g_oLogFile
, "%4d-%02d-%02d %02d:%02d:%02d,", telemetryItem
.datetime
.year
, telemetryItem
.datetime
.month
, telemetryItem
.datetime
.day
, telemetryItem
.datetime
.hour
, telemetryItem
.datetime
.min
, telemetryItem
.datetime
.sec
);
255 else if (sensor
.prec
== 2) {
256 div_t qr
= div((int)telemetryItem
.value
, 100);
257 if (telemetryItem
.value
< 0) f_printf(&g_oLogFile
, "-");
258 f_printf(&g_oLogFile
, "%d.%02d,", abs(qr
.quot
), abs(qr
.rem
));
260 else if (sensor
.prec
== 1) {
261 div_t qr
= div((int)telemetryItem
.value
, 10);
262 if (telemetryItem
.value
< 0) f_printf(&g_oLogFile
, "-");
263 f_printf(&g_oLogFile
, "%d.%d,", abs(qr
.quot
), abs(qr
.rem
));
266 f_printf(&g_oLogFile
, "%d,", telemetryItem
.value
);
272 for (uint8_t i
=0; i
<NUM_STICKS
+NUM_POTS
+NUM_SLIDERS
; i
++) {
273 f_printf(&g_oLogFile
, "%d,", calibratedAnalogs
[i
]);
276 #if defined(PCBTARANIS) || defined(PCBHORUS)
277 for (uint8_t i
=0; i
<NUM_SWITCHES
; i
++) {
278 if (SWITCH_EXISTS(i
)) {
279 f_printf(&g_oLogFile
, "%d,", getSwitchState(i
));
282 f_printf(&g_oLogFile
, "0x%08X%08X,", getLogicalSwitchesStates(32), getLogicalSwitchesStates(0));
284 f_printf(&g_oLogFile
, "%d,%d,%d,%d,%d,%d,%d,",
291 GET_2POS_STATE(TRN
));
294 div_t qr
= div(g_vbat100mV
, 10);
295 int result
= f_printf(&g_oLogFile
, "%d.%d\n", abs(qr
.quot
), abs(qr
.rem
));
297 if (result
<0 && !error_displayed
) {
298 error_displayed
= STR_SDCARD_ERROR
;
299 POPUP_WARNING(STR_SDCARD_ERROR
);
305 error_displayed
= nullptr;
306 if (g_oLogFile
.obj
.fs
) {