-
Notifications
You must be signed in to change notification settings - Fork 215
/
OpenLog_v2.pde
2330 lines (1927 loc) · 78.3 KB
/
OpenLog_v2.pde
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
12-3-09
Copyright SparkFun Electronics© 2010
Nathan Seidle
spark at sparkfun.com
OpenLog is a simple serial logger based on the ATmega328 running at 16MHz. The ATmega328
should be able to talk to high capacity (larger than 2GB) SD cards. The whole purpose of this
logger was to create a logger that just powered up and worked. OpenLog ships with standard
57600bps serial bootloader running at 16MHz so you can load new firmware with a simple serial
connection. This makes it compatible with Arduino if needed.
OpenLog runs at 9600bps by default. This is configurable to 2400, 9600, 57600, and 115200bps. We recommend
you attach a serial connection to reconfigure the unit to work at a different serial speed, but you
should be able to do it in software.
Type '?' to get a list of supported commands.
During power up, you will see '12>'. '1' indicates the serial connection is established. '2' indicates
the SD card has been successfully initialized. '>' indicates OpenLog is ready to receive commands.
Recording constant 115200bps datastreams are supported. Throw it everything you've got!
'cd ..' is a bit weird. Normally it's 'cd..' but to change to a lower dir, use 'cd ..'
Currently, the firmware supports creating a new file or directory up to 16 characters including the
'.' and extension. "123456789012.txt" is the longest name. Any longer and the module will re-initialize
as if there was a variable sizeof error.
Capital letters, white space, and other characters are supported ("Hi there#$_.txt").
Type 'set' to enter baud rate configuration menu. Select the baud rate and press enter. You will then
see a message 'Going to 9600bps...' or some such message. You will need to power down OpenLog, change
your system UART settings to match the new OpenLog baud rate and then power OpenLog back up.
If you get OpenLog stuck into an unknown baudrate, there is a safety mechanism built-in. Tie the RX pin
to ground and power up OpenLog. You should see the LEDs blink back and forth for 2 seconds, then blink
in unison. Now power down OpenLog and remove the RX/GND jumper. OpenLog is now reset to 9600bps.
Please note: The preloaded STK500 serial bootloader is 2k, and begins at 0x7800 (30,720). If the code is
larger than 30,719 bytes, you will get verification errors during serial bootloading.
SD vs HCSD configuration is found in sd_raw_config.h - Currently only 512MB, 1GB, 2GB, and some
4GB cards work (not yet compatible with HCSD cards).
STAT1 LED is sitting on PD5 (Arduino D5) - toggles when character is received
STAT2 LED is sitting on PB5 (Arduino D13) - toggles when SPI writes happen
LED Flashing errors @ 2Hz:
No SD card - 3 blinks
Baud rate change (requires power cycle) - 4 blinks
During an append, OpenLog will buffer 512 characters at a time. That means that if the system loses power
while reading in characters, you may loose up to, but no more than, 511 characters. This is important for low
power systems where you may not know when the battery or power will die. OpenLog should record each buffer as
it receives each 512 byte chunk. The only way to exit an append is with Ctrl+z (ASCII 26).
8mA idle
18mA actively writing
Input voltage on VCC can be 3.3 to 12V. Input voltage on RX-I pin must not exceed 6V. Output voltage on
TX-O pin will not be greater than 3.3V. This may cause problems with some systems - for example if your
attached microcontroller requires 4V minimum for serial communication (this is rare).
v1.1
Adding better defines for EEPROM settings
Adding new log and sequential log functions.
Code is acting very weird with what looks to be stack crashes. I can get around this by turning the optimizer off ('0').
Found an error : EEPROM functions fail when optimizer is set to '0' or '1'.
sd-reader_config.h contains flag for USE_DYNAMIC_MEMORY
Looks like tweaking the optimization higher causes the asm("nop"); to fail inside the append_file routine. Changing this to
delay_us(1); works.
I have a sneaking suspicion that I was having buffer overrun problems when defining the input_buffer at 1024 bytes. The
ATmega328 should have enough RAM (2K) but weird reset errors were occuring. With the buffer at 512bytes, append_file runs
just fine and is able to log at 115200 at a constant data rate.
Added file called 'lots-o-text.txt' to version control. This text contains easy to scan text to be used for full data
rate checking and testing.
v1.2
ringp added:
Adding support for splitting command line parameters into arguments
Adding support for reading files sequencially
read <filename> <start> <length>
New log now for sequencial log functions supports 0-65535 files
Adding support for wildcard listing or deletion of files
ls <wildcard search>
rm <wildcard delete>
Really great additions. Thanks ringp!
Nate added error testing within newlog()
Checks to see if we have 65534 logs. If so, error out to command prompt with "!Too many logs:1"
v1.3
Added sd_raw_sync() inside append_file. I believe this was why tz1's addition of the timeout buffer update feature
was not working. Auto buffer update now working. So if you don't send anything to OpenLog for 5 seconds,
the buffer will automatically record/update.
Need to create 'Testing' page to outline the battery of tests we need to throw at any OpenLog after a firmware
submission and update is complete.
Testing
create 40,000 logs
Record at full speed:
Run at full 115200, load lotsoftext.txt and verify no characters are dropped.
Detect too many logs:
Create new log at 65533 (do this by editing 'zero' function to set EEPROM to 0xFF and oxFD)
and power cycle. Verify unit starts new log. Power cycle and verify unit errors out and drops to command prompt.
Record buffer after timeout:
Create new log. Type 20 characters and wait 5 seconds. Unit should auto-record buffer. Power down unit.
Power up unit and verify LOG has 20 characters recorded.
v1.4
Added exit options to the two menus (set and baud)
Also added display of current settin to two menus (Ex: "Baud currently: 57600bps")
Added '!' infront of 'error opening'. This pops up if there is an error while trying to append
to a freshly created new log (ex: LOG00548.txt is created, then errors out because it cannot append).
'!' was added so that user can parse against it.
Replicated logging errors at 57600 using 5V Arduino
Unit would systematically glitch during logging of 111054 bytes
Increasing buffer to 1000 characters caused URU error.
URU: Unit Resets Unexpectedly
To recreate URU error. Type "append ". Include the space. If you get "!error opening", then things are
fine. If you get "!error opening#" where # is a weird character, then type 'ls' and the unit will
unexpectedly reset (URU error). I believe this is attributed to a memory overrun somewhere in the
FAT system.
Changed buffer size to 900 and declared the character buffer as volatile
#define BUFF_LEN 900
volatile char input_buffer[BUFF_LEN];
This increase to the buffer allows for clean logging of 444055 bytes with no URU errors.
Experimenting with Scott's SD cards (customer gave cards on loan for recreating logging errors):
Card with single ~740mb file produces errors when trying to open/append to new log.
Card with less stuff on it logs full 444055 bytes correctly.
v1.5
Added 4800bps and 19200bps support
Added power saving features. Current consumption at 5V is now:
In default append mode:
6.6/5.5mA while receiving characters (LED On/Off)
2.1mA during idle
In command mode: 3.2/2.1mA (LED On/Off)
So if you're actively throwing characters at the logger, it will be ~6mA. If you send the logger
characters then delay 5-10 seconds, current will be ~2.5mA. (Unit records the characters in the buffer
and goes into idle more if no characters are received after 5 seconds)
These power savings required some significant changes to uart.c / uart_getc()
v1.51 check_emergency_reset, default break character is ctrl+z 3 times, example Arduino sketch
Added function from mungewell - check_emergency_reset. This has improved testing of the RX pin.
There was potential to get a false baud reset. There is still a chance but it's now much less likely.
If OpenLog is attached to a Arduino, during bootloading of the Arduino, ctrl+z will most likely be sent
to the Arduino from the computer. This character will cause OpenLog to drop to command mode - probably
not what we want. So I added user selectable character (ctrl+x or '$' for example) and I added
user selectable number of escape characters to look for in a row (example is 1 or 2 or 3, '$$$' is a
common escape sequence). The default is now ctrl+z sent 3 times in a row.
Added an example Arduino sketch (from ScottH) to GitHub so that people can easily see how to send characters to
OpenLog. Not much to it, but it does allow us to test large amounts of text thrown at OpenLog
at 57600bps.
v1.6 Adding config file.
What happens if I reset the system by pulling RX low, but the config file has corrupt values in it?
If config file has corrupt values in it, system will default to known values 9600/ctrl+z/3/newlog
If config file is empty, system resets to known values
After some massive testing, and lots of code to check for illegal states, it looks to be pretty stable.
The only problem is that we're running out of RAM. The buffer had to be decreased from 900 bytes
to 700 bytes to facilitate all the config file checking. Testing at 57600bps, unit runs very well
over 40kb test file on straight RS232 connection. That's pretty good. Testing at 115200 on straight
connection, unit will drop a buffer every once and a while. Not great, but not much we can do if the
SD card times out for ~150ms while it's writing.
8 bits to the byte plus a start/stop bit = 10 bits per byte
@ 9600bps = 960 bytes per second. Buffer will last for 729ms
@ 57600bps = 5760 bytes per second. Buffer will last for 121ms
@ 115200bps = 11520 bytes per second. Buffer will last for 60.7ms
So if the SD card pauses for more than 60ms, 115200 will have lost data, sometimes. All other baud rates
should be covered for the most part.
SD cards with larges amounts of data will have increased pause rates. Always use a clean card where possible.
v1.61 Small PCB change. Fixed version # in help menu.
Fixed the firmware version in the help menu to v1.61.
Updated Eagle files to Eagle v5.9. Fixed weird airwire. Changed D1 LED from Green to Blue.
Will only affect new production after 4-28-10.
Closed some tickets and wrote some more example Arduino code:
http://forum.sparkfun.com/viewtopic.php?t=21438
v2.0 - 25986 bytes out of 30720
Welcome to version 2! We've moved from Roland Riegel's FAT library to Bill Greiman's sdfatlib. OpenLog now works with SD cards
up to 16GB (that is the current largest microSD card we can get our hands on). OpenLog automatically detects and works with FAT16/FAT32
file systems. It also automatically works with normal SD cards as well as SDHC cards.
Almost all the previous commands have been ported from v1 to v2. The current commands that do not work:
cd.. - does not work. You can change to an upper directory, but you cannot navigate back down the tree.
cat - this command was depricated. HEX printing is now done with the 'read' command. We've added a 5th argument to select between ASCII and HEX printing.
Wild cards do not yet work. So rm and ls do not have wild cards enabled - yet. Help us out!
Porting OpenLog to work directly under Arduino to work with sdfatlib (http://code.google.com/p/sdfatlib/) by Bill Greiman.
sdfatlib intrinsically supports FAT16, FAT32 as well as SD and HCSD cards. In a word, it's amazing.
Needs to be done:
Done - Get config file reading/loading working
Document config file in wiki: if no config file is found, current system settings are used. If config is found, system switches to settings found in file. If system settings are changed, then config file is changed and system uses new settings immediately.
Done - We don't want to constantly record a new config file on each power on. Only record when there is a change.
Get cd.. working
Seperate OpenLog_v2 into multiple files
Re-test too many log files created in the newlog setting - 65535. Potentially create thousands of files and see how sdfatlib handles it.
Done - Test sequential logging.
Get wild card support working for ls and rm
Get backspace working
Test rm with directories, and directory wildcards? Maybe not.
Get power save working
Test compile on a computer that doesn't have WinAVR
Test commands:
new - works, also in sub dirs
append - works, also in sub dirs
rm - works, but does not yet support wild cards.
md - works, also in sub dirs
cd - you can change up to a sub-directory, but you cannot navigate back down the tree. The current work around is to type 'init'. This will re-init the card and set the directory back to root.
ls - works pretty well but does not yet support wild cards. Shows directories, files, and file sizes. Would be cool if it listed dirs first.
read - works well. Tested 0, 1, 2, 3, 4, and 5 arguments (included and left off). Fails gracefully. Now prints in HEX as well!
size - works well
disk - works well, prints more information than ever before!
init - works well
sync - works, but not really tested
cat - I've decided to drop this command. You can now print in hex using the read command and using a 5th argument as '1' for ASCII (default) and '2' for HEX.
set - works well
baud - works well
v2.1 - Power save not working. Fixed issue 35. Dropping characters at 57600bps.
26058 bytes out of 30720
Fixed a bug found by Salient (Issue 35). User settings where declared at chars which allowed them to be signed. If a user went from old firmware, to v2,
the safety checks would fail because the settings would be read at -1 instead of 255. Declaring user settings as byte fixed issue.
Added "a) Clear user settings" to set menu. This allows us to completely wipe an OpenLog (user settings and config file) to see how it will respond
to future firmware changes.
Improved the file 'size' command.
Sequential logging is tested and works.
Receive testing: Using the Test_Sketch found on Github, I am testing the receive reliability at different UART speeds.
We need to test a lot of received data. At 57600, 115200, and both from an Arduino (lots of time in between characters becuase of software overhead)
and from a raw serial port (almost no time in between characters). I am hoping to make sdfatlib hiccup at 115200, full speed, across a 1MB file. If
I can make it fail, then we can start to increase the buffer size and look at how much RAM sdfatlib has left open for the buffer.
9600bps from Arduino works fine
57600bps from Arduino drops characters
115200 from Arduino drops characters
It seems that sdfatlib takes longer to write to the SD card than the original file system from Robert Reigel. I'm thinking perhaps
we should create a version of OpenLog firmware that is just sequantial logging, no fancy system menus... It might open up some RAM.
If only we could get control of the UART from Arduino's clutches, we could probably handle the ring buffer much better. Not sure how to handle UART
interrupts without tearing out HardwareSerial.cpp.
Added README to the Test sketch. Added 115200bps to test sketch.
v2.11 Tested with 16GB microSD. Fixed some general bugs. Lowered power consumption.
26136 bytes out of 30720
Fixed issue 30. I added printing a period ('.') for non-visible ASCII characters during a 'read' command. This cleans up the output a bit. HEX
printing is still available.
Fixed issue 34. When issuing a long command such as "read log00056.txt 100 200 2" (read from spot 100 to spot 200 and print in HEX), the
command shell would die at 24 spots. I increased both the general_buffer and 'buffer' in the command shell from 24 to 30. The limit is now
30 characters, so "read log00056.txt 100 20000 2" is allowed.
Works with a 16GB microSD card! High volume test: loaded 16GB card with 5GB of data. Basic serial tests work. When running at 57600, there
is an odd delay. I think it has to do with the file system doing an initial scan for an available cluster. Takes 2-3 seconds before OpenLog
returns to normal. This can cause significant data loss.
Fixing power management in v2. Power down after no characters for 3 seconds now works. Unit drops to 2.35mA in sleep. 7.88mA in sitting
RX mode (awake but not receiving anything). There is still a weird bug where the unit comes on at 30mA. After sleep, it comes back at the
correct 7-8mA. Is something not getting shut off?
v2.2 Modified append_file() to use a single buffer. Increased HardwareSerial.cpp buffer to 512 bytes.
More testing at 57600. Record times look to be 2, 5, and zero milliseconds when doing a record. This means that the split buffer doesn't
really make a difference? There are some records that take 150ms, 14ms, etc. At 57600bps, that's 7200 bytes/s, 138us per byte. With a 150ms
pause, that's 1,086 bytes that need to be buffered while we wait... Grrr. Too many.
I re-wrote the append_file function to use only one buffer. This allows us to more quickly pull data from the hardware serial buffer. Hardware
serial buffer has to be increased manually to 512. This file (hardwareserial.cpp) is stored in the Arduino directory. With testing,
it seems like recording is working more solidly at 57600bps. But now I'm seeing glitches at 19200bps so more testing is required before we
make this the official OpenLog release.
Moved input_buffer into within the append function. Attempting to shave bytes of RAM.
v2.21 ringp fork brought in. Wildcard remove and list commands now work. Remove directory now works! Change directory up/down the tree works again.
28440 bytes used of 30720.
ringp brought back many great commands! Thanks ringp!
rm LOG*.* works
ls *.TXT works
cd .. works again
ls now correctly shows directories first and files following the directories.
To remove a directory, you have to navigate into that directory. For example:
>cd TEMP (you are now in TEMP directory)
>rm -f TEMP (you are now removing TEMP, and you will be pushed back down one level of the tree)
>ls (shows files and directories where TEMP directory used to be, but TEMP directory should be gone)
ringp added new commands:
efcount: gives the number of files in the current directory. Example: "efcount" returns "count|3". There are 3 files in the current directory.
efinfo <spot>: gives info about the file in <spot>. Example: "efinfo 2" reports "LOG00588.TXT|45". File number 2 is named LOG00588.TXT and is 45 bytes in size.
verbose <"on"|"off">: sets whether command errors are verbose (long winded) or just the "!" character. Example: "verbose off" sets verbose off. Then if a
command like "blah" is received, then only "!>" is seen from the command line interface. This makes it easier for embedded systems to recognize there
was an error. This setting is not recorded to EEPROM.
*/
#include "SdFat.h"
#include "SdFatUtil.h" // use functions to print strings from flash memory
#include <EEPROM.h>
#include <avr/pgmspace.h> //Needed for strcmp_P - string compare in program space
#include <ctype.h> //Needed for isdigit
#include <avr/sleep.h> //Needed for sleep_mode
#include <avr/power.h> //Needed for powering down perihperals such as the ADC/TWI and Timers
//Debug turns on (1) or off (0) a bunch of verbose debug statements. Normally use (0)
//#define DEBUG 1
#define DEBUG 0
//Boot time testing is used to see how long it takes to go from power on to ready to receive characters
//The problem with this is that millis() is not running while the bootloader is timing out...
//Current testing shows that time to boot is around 74ms from a hardware reset and 217ms from a power cycle
//#define BOOTTIME_TESTING 1
#define BOOTTIME_TESTING 0
//The bigger the receive buffer, the less likely we are to drop characters at high speed. However, the ATmega has a limited amount of
//RAM. This debug mode allows us to view available RAM at various stages of the program
//#define RAM_TESTING 1 //On
#define RAM_TESTING 0 //Off
#define sbi(port, port_pin) ((port) |= (uint8_t)(1 << port_pin))
#define cbi(port, port_pin) ((port) &= (uint8_t)~(1 << port_pin))
char general_buffer[30];
#define FOLDER_TRACK_DEPTH 15
char folderTree[FOLDER_TRACK_DEPTH][12];
#define CFG_FILENAME "config.txt"
//EEPROM locations for the various user settings
#define LOCATION_BAUD_SETTING 0x01
#define LOCATION_SYSTEM_SETTING 0x02
#define LOCATION_FILE_NUMBER_LSB 0x03
#define LOCATION_FILE_NUMBER_MSB 0x04
#define LOCATION_ESCAPE_CHAR 0x05
#define LOCATION_MAX_ESCAPE_CHAR 0x06
#define BAUD_2400 0
#define BAUD_9600 1
#define BAUD_57600 2
#define BAUD_115200 3
#define BAUD_4800 4
#define BAUD_19200 5
#define MODE_NEWLOG 0
#define MODE_SEQLOG 1
#define MODE_COMMAND 2
#define MAX_COUNT_COMMAND_LINE_ARGS 5
#define FALSE -1
#define TRUE 0
//STAT1 is a general LED and indicates serial traffic
#define STAT1 5 //On PORTD
#define STAT1_PORT PORTD
#define STAT2 5 //On PORTB
#define STAT2_PORT PORTB
int statled1 = 5; //This is the normal status LED
int statled2 = 13; //This is the SPI LED, indicating SD traffic
//Blinking LED error codes
#define ERROR_SD_INIT 3
#define ERROR_NEW_BAUD 5
// Using OpenLog in an embedded environment
#define INCLUDE_SIMPLE_EMBEDDED
#define ECHO 0x01
#define EXTENDED_INFO 0x02
#define READABLE_TEXT_ONLY 0x04
#ifdef INCLUDE_SIMPLE_EMBEDDED
#define EMBEDDED_END_MARKER 0x08
#endif
Sd2Card card;
SdVolume volume;
SdFile currentDirectory;
SdFile file;
byte setting_uart_speed; //This is the baud rate that the system runs at, default is 9600
byte setting_system_mode; //This is the mode the system runs in, default is MODE_NEWLOG
byte setting_escape_character; //This is the ASCII character we look for to break logging, default is ctrl+z
byte setting_max_escape_character; //Number of escape chars before break logging, default is 3
#if BOOTTIME_TESTING
unsigned long boottime; //Used to keep track of how long it takes to boot
#endif
//Used for wild card delete and search
struct command_arg
{
char* arg; //Points to first character in command line argument
uint8_t arg_length; // Length of command line argument
};
static struct command_arg cmd_arg[MAX_COUNT_COMMAND_LINE_ARGS];
uint8_t feedback_mode = (ECHO | EXTENDED_INFO);
#define MIN(a,b) ((a)<(b))?(a):(b)
// 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.print(card.errorCode(), HEX);
Serial.print(',');
Serial.println(card.errorData(), HEX);
}
while(1);
}
void setup(void)
{
pinMode(statled1, OUTPUT);
//Power down various bits of hardware to lower power usage
set_sleep_mode(SLEEP_MODE_IDLE);
sleep_enable();
//Shut off TWI, Timer2, Timer1, ADC
power_twi_disable();
power_timer1_disable();
power_timer2_disable();
power_adc_disable();
check_emergency_reset(); //Look to see if the RX pin is being pulled low
read_system_settings(); //Load all system settings from EEPROM
//Setup UART
if(setting_uart_speed == BAUD_2400) Serial.begin(2400);
if(setting_uart_speed == BAUD_4800) Serial.begin(4800);
if(setting_uart_speed == BAUD_9600) Serial.begin(9600);
if(setting_uart_speed == BAUD_19200) Serial.begin(19200);
if(setting_uart_speed == BAUD_57600) Serial.begin(57600);
if(setting_uart_speed == BAUD_115200) Serial.begin(115200);
Serial.print("1");
#if RAM_TESTING
PgmPrint("Free RAM start: ");
Serial.println(FreeRam());
#endif
//Setup SD & FAT
if (!card.init()) {
PgmPrint("error card.init");
blink_error(ERROR_SD_INIT);
} // initialize the SD card
if (!volume.init(card)) {
PgmPrint("error volume.init");
blink_error(ERROR_SD_INIT);
} // initialize a FAT volume
if (!currentDirectory.openRoot(volume)) {
PgmPrint("error openRoot");
blink_error(ERROR_SD_INIT);
} // open the root directory
Serial.print("2");
#if RAM_TESTING
PgmPrint("Free RAM post SD init: ");
Serial.println(FreeRam());
#endif
//Search for a config file and load any settings found. This will over-ride previous EEPROM settings if found.
read_config_file();
memset(folderTree, 0, sizeof(folderTree));
}
void loop(void)
{
//If we are in new log mode, find a new file name to write to
if(setting_system_mode == MODE_NEWLOG)
newlog();
//If we are in sequential log mode, determine if seqlog.txt has been created or not, and then open it for logging
if(setting_system_mode == MODE_SEQLOG)
seqlog();
//Once either one of these modes exits, go to normal command mode, which is called by returning to main()
command_shell();
while(1); //We should never get this far
}
int8_t getNextFolderTreeIndex()
{
int8_t i;
for (i = 0; i < FOLDER_TRACK_DEPTH; i++)
if (strlen(folderTree[i]) == 0)
return i;
if (i >= FOLDER_TRACK_DEPTH)
i = -1;
return i;
}
//Log to a new file everytime the system boots
//Checks the spots in EEPROM for the next available LOG# file name
//Updates EEPROM and then appends to the new log file.
//Limited to 65535 files but this should not always be the case.
void newlog(void)
{
uint8_t msb, lsb;
uint16_t new_file_number;
//Combine two 8-bit EEPROM spots into one 16-bit number
lsb = EEPROM.read(LOCATION_FILE_NUMBER_LSB);
msb = EEPROM.read(LOCATION_FILE_NUMBER_MSB);
new_file_number = msb;
new_file_number = new_file_number << 8;
new_file_number |= lsb;
//If both EEPROM spots are 255 (0xFF), that means they are un-initialized (first time OpenLog has been turned on)
//Let's init them both to 0
if((lsb == 255) && (msb == 255))
{
new_file_number = 0; //By default, unit will start at file number zero
EEPROM.write(LOCATION_FILE_NUMBER_LSB, 0x00);
EEPROM.write(LOCATION_FILE_NUMBER_MSB, 0x00);
}
//The above code looks like it will forever loop if we ever create 65535 logs
//Let's quit if we ever get to 65534
//65534 logs is quite possible if you have a system with lots of power on/off cycles
if(new_file_number == 65534)
{
//Gracefully drop out to command prompt with some error
PgmPrint("!Too many logs:1!");
return; //Bail!
}
//If we made it this far, everything looks good - let's start testing to see if our file number is the next available
//Search for next available log spot
char new_file_name[] = "LOG00000.TXT";
while(1)
{
new_file_number++;
if(new_file_number > 65533) //There is a max of 65534 logs
{
PgmPrint("!Too many logs:2!");
return;
}
sprintf(new_file_name, "LOG%05d.TXT", new_file_number); //Splice the new file number into this file name
//Try to open file, if fail (file doesn't exist), then break
if (file.open(currentDirectory, new_file_name, O_CREAT | O_EXCL | O_WRITE)) break;
}
file.close(); //Close this new file we just opened
//file.writeError = false; // clear any write errors
//Record new_file number to EEPROM
lsb = (uint8_t)(new_file_number & 0x00FF);
msb = (uint8_t)((new_file_number & 0xFF00) >> 8);
EEPROM.write(LOCATION_FILE_NUMBER_LSB, lsb); // LSB
if (EEPROM.read(LOCATION_FILE_NUMBER_MSB) != msb)
EEPROM.write(LOCATION_FILE_NUMBER_MSB, msb); // MSB
#if DEBUG
PgmPrint("\nCreated new file: ");
Serial.println(new_file_name);
#endif
//Begin writing to file
append_file(new_file_name);
}
//Log to the same file every time the system boots, sequentially
//Checks to see if the file SEQLOG.txt is available
//If not, create it
//If yes, append to it
//Return 0 on error
//Return anything else on sucess
void seqlog(void)
{
char seq_file_name[13] = "SEQLOG00.TXT";
//Try to create sequential file
if (!file.open(currentDirectory, seq_file_name, O_CREAT | O_WRITE))
{
PgmPrint("Error creating SEQLOG\n");
return;
}
file.close(); //Close this new file we just opened
append_file(seq_file_name);
}
//Appends a stream of serial data to a given file
//Assumes the currentDirectory variable has been set before entering the routine
//We use the RX interrupt and a circular buffer of 1024 bytes so that we can capture a full stream of
//data even at 115200bps
//Does not exit until Ctrl+z (ASCII 26) is received
//Returns 0 on error
//Returns 1 on success
uint8_t append_file(char* file_name)
{
//44051
//This is the size of the receive buffer. The bigger it is, the less likely we will overflow the buffer while doing a record to SD.
//But we have limited amounts of RAM (~1100 bytes)
#define BUFF_LEN 50 //50 works well. Too few and we will call file.write A LOT. Too many and we run out of RAM.
//#define BUFF_LEN 400 //Fails horribly for some reason. Oh right, readSpot only goes to 255.
//#define BUFF_LEN 100 //Works well.
char inputBuffer[BUFF_LEN];
char escape_chars_received = 0;
byte readSpot = 0; //Limited to 255
byte incomingByte;
// 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(currentDirectory, file_name, O_CREAT | O_APPEND | O_WRITE)) {
if ((feedback_mode & EXTENDED_INFO) > 0)
error("open1");
}
#if DEBUG
PgmPrintln("File open");
PgmPrintln("Recording");
#endif
Serial.print('<'); //give a different prompt to indicate no echoing
digitalWrite(statled1, HIGH); //Turn on indicator LED
//Clear out the serial buffer
Serial.flush();
//Start recording incoming characters
//HardwareSerial.cpp has a buffer tied to the interrupt. We increased this buffer to 512 bytes
//As characters come in, we read them in and record them to FAT.
while(1){
uint16_t timeout_counter = 0;
#if BOOTTIME_TESTING
boottime = millis(); //Get the time from power on to ready to receive
PgmPrint("Time until ready to recieve (ms): ");
Serial.println(boottime);
#endif
#if RAM_TESTING
PgmPrint("Free RAM receive ready: ");
Serial.println(FreeRam());
#endif
//Testing to try to get rid of long delay after the first synch
//This forces OpenLog to scan the fat table and get the pointers ready before the wave of data starts coming in
//currentDirectory.sync();
//file.write("123", 3);
//file.sync();
//file.close();
//if (!file.open(currentDirectory, file_name, O_CREAT | O_APPEND | O_WRITE)) error("open1");
while(!Serial.available()){ //Wait for characters to come in
if(timeout_counter++ > 1200){ //If we haven't seen a character for about 3 seconds, then record the buffer, sync the SD, and shutdown
timeout_counter = 0;
if(readSpot != 0){ //There is unrecorded stuff sitting in the buffer
//Record the buffer
if(file.write((byte*)inputBuffer, readSpot) != readSpot)
if ((feedback_mode & EXTENDED_INFO) > 0)
PgmPrintln("error writing to file");
}
file.sync(); //Push these new file.writes to the SD card
Serial.flush(); //Clear out the current serial buffer. This is needed if the buffer gets overrun. OpenLog will fail to read the escape character if
//the buffer gets borked.
//Reset the points so that we don't record these freshly recorded characters a 2nd time, when the unit receives more characters
readSpot = 0;
//Now power down until new characters to arrive
while(!Serial.available()){
digitalWrite(statled1, LOW); //Turn off stat LED to save power
sleep_mode(); //Stop everything and go to sleep. Wake up if serial character received
}
}
delay(1); //Hang out for a ms
}
incomingByte = Serial.read(); //Grab new character from hardwareserial.cpp buffer (could be 512 bytes)
//Scan for escape character
if(incomingByte == setting_escape_character){
#if DEBUG
Serial.print("!");
#endif
if(++escape_chars_received == setting_max_escape_character) break;
}
else
escape_chars_received = 0;
inputBuffer[readSpot++] = incomingByte; //Record character to the local buffer
if(readSpot == BUFF_LEN){ //If we've filled the local small buffer, pass it to the sd write function.
//Record the buffer
if(file.write((byte*)inputBuffer, BUFF_LEN) != BUFF_LEN){
if ((feedback_mode & EXTENDED_INFO) > 0)
PgmPrintln("error writing to file");
break;
}
readSpot = 0; //Wrap the buffer
}
STAT1_PORT ^= (1<<STAT1); //Toggle the STAT1 LED each time we receive a character
} //End while - escape character received or error
//Upon receiving the escape character, we may still have stuff left in the buffer, record the last of the buffer to memory
if(readSpot != BUFF_LEN){
//Record the buffer
if(file.write((byte*)inputBuffer, readSpot) != readSpot)
if ((feedback_mode & EXTENDED_INFO) > 0)
PgmPrintln("error writing to file");
}
file.sync();
file.close(); //Done recording, close out the file
digitalWrite(statled1, LOW); //Turn off indicator LED
#if DEBUG
PgmPrintln("Done!");
#endif
PgmPrint("~"); //Indicate a successful record
return(1); //Success!
}
uint8_t gotoDir(char *dir)
{
SdFile subDirectory;
uint8_t tmp_var = 0;
//Goto parent directory
//@NOTE: This is a fix to make this work. Should be replaced with
//proper handling. Limitation: FOLDER_TRACK_DEPTH subfolders
if (strncmp_P(dir, PSTR(".."), 2) == 0) {
tmp_var = 1;
//close file system
currentDirectory.close();
// open the root directory
if (!currentDirectory.openRoot(volume)) error("openRoot");
int8_t index = getNextFolderTreeIndex() - 1;
if (index >= 0)
{
for (int8_t iTemp = 0; iTemp < index; iTemp++)
{
if (!(tmp_var = subDirectory.open(currentDirectory, folderTree[iTemp], O_READ)))
break;
currentDirectory = subDirectory; //Point to new directory
subDirectory.close();
}
memset(folderTree[index], 0, 11);
}
if (((feedback_mode & EXTENDED_INFO) > 0) && (tmp_var == 0))
{
PgmPrint("cannot cd to parent directory: ");
Serial.println(dir);
}
}
else
{
if (!(tmp_var = subDirectory.open(currentDirectory, dir, O_READ))) {
if ((feedback_mode & EXTENDED_INFO) > 0)
{
PgmPrint("directory not found: ");
Serial.println(dir);
}
}
else
{
currentDirectory = subDirectory; //Point to new directory
int8_t index = getNextFolderTreeIndex();
if (index >= 0)
strncpy(folderTree[index], dir, 11);
}
}
return tmp_var;
}
void command_shell(void)
{
//provide a simple shell
char buffer[30];
uint8_t tmp_var;
#ifdef INCLUDE_SIMPLE_EMBEDDED
uint32_t file_index;
uint8_t command_succedded = 1;
#endif //INCLUDE_SIMPLE_EMBEDDED
while(true)
{
#ifdef INCLUDE_SIMPLE_EMBEDDED
if ((feedback_mode & EMBEDDED_END_MARKER) > 0)
Serial.print((char)0x1A); // Ctrl+Z ends the data and marks the start of result
if (command_succedded == 0)
Serial.print('!');
#endif
Serial.print('>');
//read command
char* command = buffer;
if(read_line(command, sizeof(buffer)) < 1)
{
#ifdef INCLUDE_SIMPLE_EMBEDDED
command_succedded = 1;
#endif
continue;
}
#ifdef INCLUDE_SIMPLE_EMBEDDED
command_succedded = 0;
#endif
//Argument 1: The actual command
char* command_arg = get_cmd_arg(0);
//execute command
if(strcmp_P(command_arg, PSTR("init")) == 0)
{
PgmPrintln("Closing down file system");
//close file system
currentDirectory.close();
// open the root directory
if (!currentDirectory.openRoot(volume)) error("openRoot");
PgmPrintln("File system initialized");
#ifdef INCLUDE_SIMPLE_EMBEDDED
command_succedded = 1;
#endif
}
else if(strcmp_P(command_arg, PSTR("?")) == 0)
{
//Print available commands
print_menu();
#ifdef INCLUDE_SIMPLE_EMBEDDED
command_succedded = 1;
#endif
}
else if(strcmp_P(command_arg, PSTR("help")) == 0)
{
//Print available commands
print_menu();
#ifdef INCLUDE_SIMPLE_EMBEDDED
command_succedded = 1;
#endif
}
else if(strcmp_P(command_arg, PSTR("baud")) == 0)
{
//Go into baud select menu
baud_menu();
#ifdef INCLUDE_SIMPLE_EMBEDDED
command_succedded = 1;
#endif
}
else if(strcmp_P(command_arg, PSTR("set")) == 0)
{
//Go into system setting menu
system_menu();
#ifdef INCLUDE_SIMPLE_EMBEDDED
command_succedded = 1;
#endif
}
#if DEBUG
else if(strcmp_P(command_arg, PSTR("create")) == 0)
{
//Test for creating the maximum number of files that a directory can hold
//create_lots_of_files();
}
#endif
else if(strcmp_P(command_arg, PSTR("ls")) == 0)
{
if ((feedback_mode & EXTENDED_INFO) > 0)
{
PgmPrint("Volume is FAT");
Serial.println(volume.fatType(), DEC);
}
currentDirectory.ls(LS_SIZE, 0, &wildcmp, get_cmd_arg(1));
#ifdef INCLUDE_SIMPLE_EMBEDDED
command_succedded = 1;
#endif
}
else if(strncmp_P(command_arg, PSTR("md"), 2) == 0)
{
//Expecting only 2 arguments
if (too_many_arguments_error(2, command))
continue;
//Argument 2: Directory name
command_arg = get_cmd_arg(1);
if(command_arg == 0)
continue;
SdFile newDirectory;
if (!newDirectory.makeDir(currentDirectory, command_arg)) {
if ((feedback_mode & EXTENDED_INFO) > 0)
{
PgmPrintln("error creating directory: ");
Serial.println(command_arg);
}
}
#ifdef INCLUDE_SIMPLE_EMBEDDED
else
{
command_succedded = 1;
}
#endif
}
else if(strncmp_P(command_arg, PSTR("rm"), 2) == 0)
{
//Expecting max 3 arguments
if (too_many_arguments_error(3, command))
continue;