├── .gitignore
├── CrashTracking
├── .gitignore
├── CrashTracking.ino
├── CrashTracking.vcxproj.filters
├── Program.cpp
├── ApplicationMonitor.h
├── CrashTracking.vcxproj
└── ApplicationMonitor.cpp
├── README.md
├── CrashTracking.sln
├── LICENSE
└── CrashTesting.mlx
/.gitignore:
--------------------------------------------------------------------------------
1 | /bin/
2 | /obj/
3 | *.sdf
4 |
--------------------------------------------------------------------------------
/CrashTracking/.gitignore:
--------------------------------------------------------------------------------
1 | /bin/
2 | /obj/
3 | .sdf
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ArduinoCrashMonitor
2 | ===================
3 |
--------------------------------------------------------------------------------
/CrashTracking/CrashTracking.ino:
--------------------------------------------------------------------------------
1 | /*
2 | Program is in Program.cpp
3 | */
4 |
--------------------------------------------------------------------------------
/CrashTracking.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2012
4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CrashTracking", "CrashTracking\CrashTracking.vcxproj", "{60C8E4F8-C1BA-44CD-B07B-ED536EB9C571}"
5 | EndProject
6 | Global
7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
8 | Debug|Win32 = Debug|Win32
9 | Release|Win32 = Release|Win32
10 | EndGlobalSection
11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
12 | {60C8E4F8-C1BA-44CD-B07B-ED536EB9C571}.Debug|Win32.ActiveCfg = Debug|Win32
13 | {60C8E4F8-C1BA-44CD-B07B-ED536EB9C571}.Debug|Win32.Build.0 = Debug|Win32
14 | {60C8E4F8-C1BA-44CD-B07B-ED536EB9C571}.Release|Win32.ActiveCfg = Release|Win32
15 | {60C8E4F8-C1BA-44CD-B07B-ED536EB9C571}.Release|Win32.Build.0 = Release|Win32
16 | EndGlobalSection
17 | GlobalSection(SolutionProperties) = preSolution
18 | HideSolutionNode = FALSE
19 | EndGlobalSection
20 | EndGlobal
21 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 Megunolink
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/CrashTracking/CrashTracking.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hpp;hxx;hm;inl;inc;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 |
18 |
19 | Source Files
20 |
21 |
22 | Source Files
23 |
24 |
25 |
26 |
27 | Header Files
28 |
29 |
30 |
--------------------------------------------------------------------------------
/CrashTracking/Program.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "ApplicationMonitor.h"
3 |
4 | Watchdog::CApplicationMonitor ApplicationMonitor;
5 |
6 | // An LED to flash while we can.
7 | const int gc_nLEDPin = 13;
8 |
9 | // countdown until the program locks up.
10 | int g_nEndOfTheWorld = 15;
11 |
12 | // number of iterations completed.
13 | int g_nIterations = 0;
14 |
15 | void setup()
16 | {
17 | Serial.begin(9600);
18 | pinMode(gc_nLEDPin, OUTPUT);
19 | Serial.println("Ready");
20 |
21 | ApplicationMonitor.Dump(Serial);
22 | ApplicationMonitor.EnableWatchdog(Watchdog::CApplicationMonitor::Timeout_4s);
23 | //ApplicationMonitor.DisableWatchdog();
24 |
25 | Serial.println("Hello World!");
26 | }
27 |
28 | void loop()
29 | {
30 | ApplicationMonitor.IAmAlive();
31 | ApplicationMonitor.SetData(g_nIterations++);
32 |
33 | Serial.println("The end is nigh!!!");
34 |
35 | digitalWrite(gc_nLEDPin, HIGH); // turn the LED on (HIGH is the voltage level)
36 | delay(200); // wait for a second
37 | digitalWrite(gc_nLEDPin, LOW); // turn the LED off by making the voltage LOW
38 | delay(200); // wait for a second
39 |
40 | if (g_nEndOfTheWorld == 0)
41 | {
42 | Serial.println("The end is here. Goodbye cruel world.");
43 | while(1)
44 | ; // do nothing until the watchdog timer kicks in and resets the program.
45 | }
46 |
47 | --g_nEndOfTheWorld;
48 | }
--------------------------------------------------------------------------------
/CrashTracking/ApplicationMonitor.h:
--------------------------------------------------------------------------------
1 | /* ************************************************************************************************
2 | ** Class to log watchdog lockup location to eeprom.
3 | ** Configures the watchdog to fire an interrupt before resetting the micro. When the interrupt
4 | ** fires, the program counter and 4 bytes of user data are written to the eeprom. The data can
5 | ** be examined when the micro restarts.
6 | ** ************************************************************************************************ */
7 |
8 | #pragma once
9 | #include
10 | #include
11 |
12 | namespace Watchdog
13 | {
14 | #if defined(__AVR_ATmega2560__)
15 | #define PROGRAM_COUNTER_SIZE 3 /* bytes*/
16 | #else
17 | #define PROGRAM_COUNTER_SIZE 2 /* bytes*/
18 | #endif
19 |
20 | struct CApplicationMonitorHeader
21 | {
22 | // The number of reports saved in the eeprom.
23 | uint8_t m_uSavedReports;
24 |
25 | // the location for the next report to be saved
26 | uint8_t m_uNextReport;
27 |
28 | } __attribute__((__packed__));
29 |
30 | struct CCrashReport
31 | {
32 | // Address of code executing when watchdog interrupt fired.
33 | // On the 328 & 644 this is just a word pointer. For the mega,
34 | // we need 3 bytes. We can use an array to make it easy.
35 | uint8_t m_auAddress[PROGRAM_COUNTER_SIZE];
36 |
37 | // User data.
38 | uint32_t m_uData;
39 |
40 | } __attribute__((__packed__));
41 |
42 | class CApplicationMonitor
43 | { // The address in the eeprom where crash data is saved. The
44 | // first byte is the number of records saved, followed by
45 | // the location for the next report to be saved, followed by
46 | // the indivual CApplicationState records.
47 | const int c_nBaseAddress;
48 |
49 | // The maximum number of crash entries stored in the eeprom.
50 | const int c_nMaxEntries;
51 | enum EConstants { DEFAULT_ENTRIES = 10 };
52 |
53 | CCrashReport m_CrashReport;
54 | public:
55 | CApplicationMonitor(int nBaseAddress = 500, int nMaxEntries = DEFAULT_ENTRIES);
56 | void Dump(Print &rDestination, bool bOnlyIfPresent = true) const;
57 |
58 | enum ETimeout
59 | {
60 | Timeout_15ms = WDTO_15MS,
61 | Timeout_30ms = WDTO_30MS,
62 | Timeout_60ms = WDTO_60MS,
63 | Timeout_120ms = WDTO_120MS,
64 | Timeout_250ms = WDTO_250MS,
65 | Timeout_500ms = WDTO_500MS,
66 | Timeout_1s = WDTO_1S,
67 | Timeout_2s = WDTO_2S,
68 | #if defined(WDTO_4S)
69 | Timeout_4s = WDTO_4S,
70 | #endif
71 | #if defined(WDTO_8S)
72 | Timeout_8s = WDTO_8S,
73 | #endif
74 | };
75 |
76 | void EnableWatchdog(ETimeout Timeout);
77 | void DisableWatchdog();
78 |
79 | void IAmAlive() const;
80 | void SetData(uint32_t uData) { m_CrashReport.m_uData = uData; }
81 | uint32_t GetData() const { return m_CrashReport.m_uData; }
82 |
83 | void WatchdogInterruptHandler(uint8_t *puProgramAddress);
84 |
85 | private:
86 | void SaveHeader(const CApplicationMonitorHeader &rReportHeader) const;
87 | void LoadHeader(CApplicationMonitorHeader &rReportHeader) const;
88 |
89 | void SaveCurrentReport(int nReportSlot) const;
90 | void LoadReport(int nReport, CCrashReport &rState) const;
91 | int GetAddressForReport(int nReport) const;
92 |
93 | void ReadBlock(int nBaseAddress, void *pData, uint8_t uSize) const;
94 | void WriteBlock(int nBaseAddress, const void *pData, uint8_t uSize) const;
95 |
96 | void PrintValue(Print &rDestination, const __FlashStringHelper *pLabel,
97 | uint32_t uValue, uint8_t uRadix, bool bNewLine) const;
98 | };
99 |
100 | }
101 |
102 | extern Watchdog::CApplicationMonitor ApplicationMonitor;
103 |
--------------------------------------------------------------------------------
/CrashTesting.mlx:
--------------------------------------------------------------------------------
1 |
2 |
3 | en-NZ
4 |
5 |
6 |
7 | true
8 | true
9 | true
10 | false
11 | false
12 | false
13 |
14 |
15 |
16 | true
17 |
18 |
19 | Crash tester
20 | CrashTracking\bin\Debug\CrashTracking.hex
21 | true
22 | Uno
23 | serial
24 | COM37
25 | 2013-09-16T21:33:28.1009248+12:00
26 | true
27 | false
28 |
29 |
30 |
31 |
32 |
33 |
34 | 0
35 |
36 |
37 | 0
38 | true
39 | RS232 0
40 | false
41 |
42 | Fixed
43 | COM37
44 |
45 | 9600
46 | 8
47 | None
48 | None
49 | One
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | 0
61 | Connection Manager
62 | false
63 |
64 |
65 | true
66 | false
67 | false
68 | false
69 | 0
70 | Monitor
71 | false
72 |
73 |
74 | 0
75 | Program Devices
76 | false
77 | false
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 | false
131 |
132 |
133 | 0
134 | d:\Users\Paul\Documents
135 | true
136 | Report {Seq}
137 | true
138 | PT1H
139 | 0001-01-01T00:00:00
140 |
141 |
142 |
143 |
--------------------------------------------------------------------------------
/CrashTracking/CrashTracking.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Release
10 | Win32
11 |
12 |
13 |
14 | $(registry:HKEY_CURRENT_USER\Software\Blue Leaf Software\Arduino Build Tools@Tools)
15 |
16 |
17 | $(VCTargetsPath)Platforms\BLSArduino\
18 |
19 |
20 |
21 |
22 |
23 | {60C8E4F8-C1BA-44CD-B07B-ED536EB9C571}
24 | CrashTracking
25 |
26 |
27 |
28 | Application
29 | true
30 | v110
31 | MultiByte
32 |
33 |
34 | Application
35 | false
36 | v110
37 | true
38 | MultiByte
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | $(ProjectDir)\bin\$(Configuration)\
56 | .elf
57 | EEPROM;SPI;SD;Ethernet
58 | $(ArduinoCorePath);$(ArduinoLibPath);$(AVRRoot)\avr\include;$(AVRRoot)\lib\gcc\avr\4.3.2\include
59 | obj\$(Configuration)\
60 | Mega2560
61 |
62 |
63 | $(ProjectDir)\bin\$(Configuration)\
64 | .elf
65 | EEPROM;SPI;SD;Ethernet
66 | $(ArduinoCorePath);$(ArduinoLibPath);$(AVRRoot)\avr\include;$(AVRRoot)\lib\gcc\avr\4.3.2\include
67 | obj\$(Configuration)\
68 |
69 |
70 | Uno
71 |
72 |
73 |
74 | Level3
75 | Disabled
76 | true
77 |
78 |
79 | true
80 |
81 |
82 |
83 |
84 | Level3
85 | MaxSpeed
86 | true
87 | true
88 | true
89 |
90 |
91 | true
92 | true
93 | true
94 |
95 |
96 |
97 |
98 | $(ArduinoVariantPath)\$(Variant);%(AdditionalIncludeDirectories)
99 | $(BuildToolsPath)ArduinoVisualStudioFixes.h
100 |
101 |
102 |
103 |
104 | $(ArduinoVariantPath)\$(Variant);%(AdditionalIncludeDirectories)
105 | $(BuildToolsPath)ArduinoVisualStudioFixes.h
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/CrashTracking/ApplicationMonitor.cpp:
--------------------------------------------------------------------------------
1 | #include "ApplicationMonitor.h"
2 | #include
3 |
4 | using namespace Watchdog;
5 |
6 | /*
7 | Function called when the watchdog interrupt fires. The function is naked so that
8 | we don't get program stated pushed onto the stack. Consequently the top two
9 | values on the stack will be the program counter when the interrupt fired. We're
10 | going to save that in the eeprom then let the second watchdog event reset the
11 | micro. We never return from this function.
12 | */
13 | uint8_t *upStack;
14 | extern "C" {
15 | void appMon_asm_gate(void) __attribute__((used));
16 | void appMon_asm_gate(void){
17 | ApplicationMonitor.WatchdogInterruptHandler(upStack);
18 | }
19 | }
20 |
21 | ISR(WDT_vect, ISR_NAKED)
22 | {
23 | // Setup a pointer to the program counter. It goes in a register so we
24 | // don't mess up the stack.
25 | upStack = (uint8_t*)SP;
26 |
27 | // The stack pointer on the AVR micro points to the next available location
28 | // so we want to go back one location to get the first byte of the address
29 | // pushed onto the stack when the interrupt was triggered. There will be
30 | // PROGRAM_COUNTER_SIZE bytes there.
31 | ++upStack;
32 |
33 | // Newer versions of GCC don't like when naked functions call regular functions
34 | // so we use call an assembly gate function instead
35 | __asm__ __volatile__ (
36 | "call appMon_asm_gate \n"
37 | );
38 | }
39 |
40 | /*
41 | Initialize the application monitor. There should only be a single instance
42 | of the application monitor in the whole program.
43 | nBaseAddress: The address in the eeprom where crash data should be stored.
44 | nMaxEntries: The maximum number of crash entries that should be stored in the
45 | eeprom. Storage of eeprom data will take up sizeof(CApplicationMonitorHeader) +
46 | nMaxEntries * sizeof(CCrashReport) bytes in the eeprom.
47 | */
48 | CApplicationMonitor::CApplicationMonitor(int nBaseAddress, int nMaxEntries)
49 | : c_nBaseAddress(nBaseAddress), c_nMaxEntries(nMaxEntries)
50 | {
51 | m_CrashReport.m_uData = 0;
52 | }
53 |
54 | /*
55 | Enable the watchdog timer & have it trigger an interrupt before
56 | resetting the micro. When the interrupt fires, we save the program counter
57 | to the eeprom.
58 | */
59 | void CApplicationMonitor::EnableWatchdog(CApplicationMonitor::ETimeout Timeout)
60 | {
61 | wdt_enable(Timeout);
62 | WDTCSR |= _BV(WDIE);
63 | }
64 |
65 | void CApplicationMonitor::DisableWatchdog()
66 | {
67 | wdt_disable();
68 | }
69 |
70 | /*
71 | Lets the watchdog timer know the program is still alive. Call this before
72 | the watchdog timeout ellapses to prevent program being aborted.
73 | */
74 | void CApplicationMonitor::IAmAlive() const
75 | {
76 | wdt_reset();
77 | }
78 |
79 | void CApplicationMonitor::Dump(Print &rDestination, bool bOnlyIfPresent) const
80 | {
81 | CApplicationMonitorHeader Header;
82 | CCrashReport Report;
83 | uint8_t uReport;
84 | uint32_t uAddress;
85 |
86 | LoadHeader(Header);
87 | if (!bOnlyIfPresent || Header.m_uSavedReports != 0)
88 | {
89 | rDestination.println(F("Application Monitor"));
90 | rDestination.println(F("-------------------"));
91 | PrintValue(rDestination, F("Saved reports: "), Header.m_uSavedReports, DEC, true);
92 | PrintValue(rDestination, F("Next report: "), Header.m_uNextReport, DEC, true);
93 |
94 | for (uReport = 0; uReport < Header.m_uSavedReports; ++uReport)
95 | {
96 | LoadReport(uReport, Report);
97 |
98 | rDestination.print(uReport);
99 | uAddress = 0;
100 | memcpy(&uAddress, Report.m_auAddress, PROGRAM_COUNTER_SIZE);
101 | PrintValue(rDestination, F(": word-address=0x"), uAddress, HEX, false);
102 | PrintValue(rDestination, F(": byte-address=0x"), uAddress * 2, HEX, false);
103 | PrintValue(rDestination, F(", data=0x"), Report.m_uData, HEX, true);
104 | }
105 | }
106 | }
107 |
108 | void CApplicationMonitor::PrintValue(Print &rDestination, const __FlashStringHelper *pLabel,
109 | uint32_t uValue, uint8_t uRadix, bool bNewLine) const
110 | {
111 | rDestination.print(pLabel);
112 | rDestination.print(uValue, uRadix);
113 | if (bNewLine)
114 | rDestination.println();
115 | }
116 |
117 | void CApplicationMonitor::WatchdogInterruptHandler(uint8_t *puProgramAddress)
118 | {
119 | CApplicationMonitorHeader Header;
120 |
121 | LoadHeader(Header);
122 | memcpy(m_CrashReport.m_auAddress, puProgramAddress, PROGRAM_COUNTER_SIZE);
123 | SaveCurrentReport(Header.m_uNextReport);
124 |
125 | // Update header for next time.
126 | ++Header.m_uNextReport;
127 | if (Header.m_uNextReport >= c_nMaxEntries)
128 | Header.m_uNextReport = 0;
129 | else
130 | ++Header.m_uSavedReports;
131 | SaveHeader(Header);
132 |
133 | // Wait for next watchdog time out to reset system.
134 | // If the watch dog timeout is too short, it doesn't
135 | // give the program much time to reset it before the
136 | // next timeout. So we can be a bit generous here.
137 | wdt_enable(WDTO_120MS);
138 | while (true)
139 | ;
140 | }
141 |
142 | void CApplicationMonitor::LoadHeader(CApplicationMonitorHeader &rReportHeader) const
143 | {
144 | ReadBlock(c_nBaseAddress, &rReportHeader, sizeof(rReportHeader));
145 |
146 | // Ensure the report structure is valid.
147 | if (rReportHeader.m_uSavedReports == 0xff) // eeprom is 0xff when uninitialized
148 | rReportHeader.m_uSavedReports = 0;
149 | else if (rReportHeader.m_uSavedReports > c_nMaxEntries)
150 | rReportHeader.m_uSavedReports = c_nMaxEntries;
151 |
152 | if (rReportHeader.m_uNextReport >= c_nMaxEntries)
153 | rReportHeader.m_uNextReport = 0;
154 | }
155 |
156 | void CApplicationMonitor::SaveHeader(const CApplicationMonitorHeader &rReportHeader) const
157 | {
158 | WriteBlock(c_nBaseAddress, &rReportHeader, sizeof(rReportHeader));
159 | }
160 |
161 | void CApplicationMonitor::SaveCurrentReport(int nReportSlot) const
162 | {
163 | WriteBlock(GetAddressForReport(nReportSlot), &m_CrashReport, sizeof(m_CrashReport));
164 | }
165 |
166 | void CApplicationMonitor::LoadReport(int nReport, CCrashReport &rState) const
167 | {
168 | ReadBlock(GetAddressForReport(nReport), &rState, sizeof(rState));
169 |
170 | // The return address is reversed when we read it off the stack. Correct that.
171 | // by reversing the byte order. Assuming PROGRAM_COUNTER_SIZE is 2 or 3.
172 | uint8_t uTemp;
173 | uTemp = rState.m_auAddress[0];
174 | rState.m_auAddress[0] = rState.m_auAddress[PROGRAM_COUNTER_SIZE - 1];
175 | rState.m_auAddress[PROGRAM_COUNTER_SIZE - 1] = uTemp;
176 | }
177 |
178 | int CApplicationMonitor::GetAddressForReport(int nReport) const
179 | {
180 | int nAddress;
181 |
182 | nAddress = c_nBaseAddress + sizeof(CApplicationMonitorHeader);
183 | if (nReport < c_nMaxEntries)
184 | nAddress += nReport * sizeof(m_CrashReport);
185 | return nAddress;
186 | }
187 |
188 | void CApplicationMonitor::ReadBlock(int nBaseAddress, void *pData, uint8_t uSize) const
189 | {
190 | uint8_t *puData = (uint8_t *)pData;
191 | while (uSize --)
192 | *puData++ = eeprom_read_byte((const uint8_t *)nBaseAddress++);
193 | }
194 |
195 | void CApplicationMonitor::WriteBlock(int nBaseAddress, const void *pData, uint8_t uSize) const
196 | {
197 | const uint8_t *puData = (const uint8_t *)pData;
198 | while (uSize --)
199 | eeprom_write_byte((uint8_t *)nBaseAddress++, *puData++);
200 | }
201 |
202 |
--------------------------------------------------------------------------------