Skip to content

Commit 3506fe0

Browse files
committed
Bumped version to 1.2.1 - Improved Stack info, if stack is exhausted, added flush to HexDump, improved examples.
1 parent a78d522 commit 3506fe0

File tree

8 files changed

+149
-46
lines changed

8 files changed

+149
-46
lines changed

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
<div align = center>
22

33
# [ArduinoUtils](https://github.com/ArminJo/Arduino-Utils)
4-
My utility collection for Arduino
4+
My utility collection for Arduino<br/>
5+
Contains utilities for fast and flexible ADC conversions, Voltage and Resitance measurement, RAM, heap and stack info, Hex dumps, EMA filters etc.
56

67
[![Badge License: GPLv3](https://img.shields.io/badge/License-GPLv3-brightgreen.svg)](https://www.gnu.org/licenses/gpl-3.0)
78
&nbsp; &nbsp;
@@ -291,6 +292,9 @@ If you are using [Sloeber](https://eclipse.baeyens.it) as your IDE, you can easi
291292

292293
# Revision History
293294

295+
### Version 1.2.1
296+
- Improved Stack info, if stack is exhausted, added flush to HexDump, improved examples.
297+
294298
### Version 1.2.0
295299
- Updated AVRUtils, ADCUtils, HexDump and ShowInfo.
296300
- Added Optiboot 8.1.

examples/AVRUtilsDemo/AVRUtilsDemo.ino

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* Demo of 2 seconds sleep with watchdog
55
* Demo of printRAMInfo() and printStackUnusedAndUsedBytes ()
66
*
7-
* Copyright (C) 2020-2024 Armin Joachimsmeyer
7+
* Copyright (C) 2020-2026 Armin Joachimsmeyer
88
* armin.joachimsmeyer@gmail.com
99
*
1010
* This file is part of Arduino-Utils https://github.com/ArminJo/Arduino-Utils.
@@ -134,12 +134,17 @@ void setup() {
134134
Serial.begin(115200);
135135
// Just to know which program is running on my Arduino
136136
Serial.println(F("START " __FILE__ "\r\nVersion " VERSION_EXAMPLE " from " __DATE__));
137+
138+
Serial.println();
139+
Serial.println(F("Print base RAM data"));
137140
printBaseRAMData(&Serial);
138141
Serial.flush();
139142

140143
initStackFreeMeasurement();
141144

142145
#endif // defined(CODE_FOR_ATTINY)
146+
Serial.println();
147+
Serial.println(F("Print MCUSR, Brown-out level and dummy array info"));
143148

144149
Serial.flush();
145150

@@ -186,33 +191,40 @@ void setup() {
186191
tone(TONE_OUT_PIN, 2200, 400);
187192
delay(400);
188193

189-
Serial.println();
190-
191194
#if !defined(CODE_FOR_ATTINY)
192-
printRAMInfo(&Serial);
193-
printStackMaxUsedAndUnusedSizes(&Serial);
195+
Serial.println();
196+
Serial.println(F("Print RAM and Stack info"));
197+
printRAMAndStackInfo(&Serial);
194198

199+
Serial.println();
195200
Serial.println(F("Dump 288 bytes of stack / end of RAM"));
196201
printStackMemory(288);
197-
printRAMInfo(&Serial);
198202

199203
Serial.println();
204+
Serial.println(F("Print RAM and Stack info, stack usage has increased"));
205+
printRAMAndStackInfo(&Serial);
200206

201207
/*
202208
* Test calloc sizes
203209
*/
210+
Serial.println();
211+
Serial.println(F("Test calloc sizes"));
204212
testCallocSizesAndPrint(&Serial);
205213

214+
Serial.println();
215+
Serial.println(F("Print base RAM data again"));
206216
printBaseRAMData(&Serial);
207-
printRAMInfo(&Serial);
208-
printStackMaxUsedAndUnusedSizes(&Serial); // test this function, it works different from function used in printRAMInfo
217+
Serial.println();
218+
Serial.println(F("Print RAM and Stack info again"));
219+
printRAMAndStackInfo(&Serial);
209220
#endif
210221

211-
Serial.println(F("Dump current stack"));
222+
Serial.println();
223+
Serial.print(F("Dump current stack: "));
212224
printStackDump();
213-
214-
Serial.println(F("Show return address for current function \"setup()\""));
225+
Serial.print(F("\"__builtin_return_address(0)\" yields the return address of the current function \"setup()\" =0x"));
215226
Serial.println((uint16_t) __builtin_return_address(0), HEX);
227+
Serial.println();
216228

217229
/*
218230
* init sleep mode and wakeup period
@@ -230,30 +242,43 @@ void setup() {
230242
* Otherwise ADC can NOT disabled by (ADCSRA = 0) anymore and always consumes 200 uA!
231243
*/
232244
// PRR = _BV(PRTIM1) | _BV(PRTIM0) | _BV(PRUSI); // Disable timer 0 and USI - has no effect on Power Down current
245+
Serial.println(F("Start of loop:"));
246+
233247
}
234248

235249
void loop() {
236250

237251
digitalWrite(LED_PIN, HIGH);
238252
delay(400);
239-
if (sNumberOfSleeps == 4) {
240-
Serial.println(F("Wait 5 seconds to force watchdog reset"));
253+
if (sNumberOfSleeps >= 4) {
254+
Serial.println(F("Wait 3 seconds to see timeout message after 2 seconds"));
255+
initTimeoutWithWatchdog(WDTO_2S);
256+
delay(3000);
257+
258+
Serial.println(F("Now wait 3 seconds to wait for watchdog reset after 2 seconds"));
259+
Serial.println();
241260
strncpy(sWatchdogResetInfoString, "4. loop", WATCHDOG_INFO_STRING_SIZE - 1);
242-
wdt_enable(WDTO_2S); // Resets after 2 seconds if wdt_disable() is not called before;
243-
delay(5000);
261+
wdt_enable(WDTO_2S); // Resets the CPU after 2 seconds. To avoid this you must call wdt_disable() or wdt_reset() before.
262+
delay(3000);
263+
264+
Serial.println(F("We will never see this, because watchdog does a reset before!"));
244265
}
245266
strncpy(sWatchdogResetInfoString, "unknown reason", WATCHDOG_INFO_STRING_SIZE - 1);
246267
digitalWrite(LED_PIN, LOW);
247-
Serial.print(F("Sleep 2 seconds with watchdog reset sNumberOfSleeps="));
268+
Serial.print(F("Sleep 2 seconds with watchdog timer sNumberOfSleeps="));
248269
Serial.println(sNumberOfSleeps);
249270
Serial.flush(); // Otherwise the USART interrupt will wake us up
250-
sleepWithWatchdog(WDTO_2S, true); // Sleep 2 seconds
271+
sleepWithWatchdog(WDTO_2S, true); // Sleep 2 seconds, then continue with loop
251272
}
252273

253274
/*
254275
* This interrupt wakes up the cpu from sleep
255276
*/
256277
ISR(WDT_vect) {
278+
if (sNumberOfSleeps >= 4) {
279+
wdt_reset(); // restart watchdog time
280+
Serial.println(F("Timeout detected!"));
281+
}
257282
sNumberOfSleeps++;
258283
}
259284

examples/HCSR04Demo/HCSR04Demo.ino

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939

4040
#include "HCSR04.hpp"
4141

42-
#if !defined(STR_HELPER)
42+
#if !defined(STR_HELPER) && !defined(STR)
4343
#define STR_HELPER(x) #x
4444
#define STR(x) STR_HELPER(x)
4545
#endif

library.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"name": "ArduinoUtils",
3+
"version": "1.2.1",
4+
"description": "Contains utilities for fast and flexible ADC conversions, Voltage and Resitance measurement, RAM, heap and stack info, Hex dumps, EMA filters etc.",
5+
"keywords": "ADC, EMA, Hex dump, RAM info",
6+
"homepage": "https://github.com/ArminJo/Arduino-Utils",
7+
"repository":
8+
{
9+
"type": "git",
10+
"url": "https://github.com/ArminJo/Arduino-Utils.git"
11+
},
12+
"authors" :
13+
[
14+
{
15+
"name":"Armin Joachimsmeyer",
16+
"email":"armin.arduino@gmail.com",
17+
"maintainer": true
18+
}
19+
],
20+
"license": "GPL-3.0-or-later",
21+
"frameworks": "arduino",
22+
"platforms": ["atmelavr"],
23+
"examples": "examples/*/*.ino",
24+
"export": {"exclude": [".github", "pictures"]}
25+
}

library.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
name=ArduinoUtils
2-
version=1.2.0
2+
version=1.2.1
33
author=Armin Joachimsmeyer
44
maintainer=Armin Joachimsmeyer <armin.arduino@gmail.com>
55
sentence=Utilities library for Arduino.
6-
paragraph=Contains utilities for fast and flexible ADC conversions,
6+
paragraph=Contains utilities for fast and flexible ADC conversions, Voltage and Resistance measurement, RAM, heap and stack info, Hex dumps, EMA filters etc.
77
category=Signal Input/Output
88
url=https://github.com/ArminJo/Arduino-Utils
99
architectures=avr

src/AVRUtils.cpp

Lines changed: 60 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@
4242

4343
uint8_t* getStartOfAvailableHeap(void) {
4444
if (__brkval == 0) {
45-
// __brkval is 0 if no malloc() has happened before
45+
// __brkval is 0 if no malloc() has happened before
4646
// __brkval = __malloc_heap_start;
47-
__brkval = &__heap_start;
47+
__brkval = &__heap_start; // = __bss_end, the linker address of heap start
4848
}
4949
return (uint8_t*) __brkval;
5050
}
@@ -87,7 +87,7 @@ int16_t getStackMaxUsedAndUnusedSizes(uint16_t *aStackUnusedSizePointer) {
8787
tStackUnused++;
8888
}
8989

90-
int16_t tStackMaxUsedSize = (RAMEND + 1) - (uint16_t) tHeapPtr;
90+
int16_t tStackMaxUsedSize = (RAMEND + 1) - (int16_t) tHeapPtr;
9191

9292
*aStackUnusedSizePointer = tStackUnused;
9393
if (tStackUnused == 0) {
@@ -109,17 +109,25 @@ void printStackMaxUsedAndUnusedSizes(Print *aSerial) {
109109
aSerial->print(F(", unused="));
110110
aSerial->print(tStackUnusedBytes);
111111
aSerial->print(F(" of current total "));
112-
aSerial->println((RAMEND + 1) - (uint16_t) getStartOfAvailableHeap());
112+
aSerial->println((RAMEND + 1) - (int16_t) getStartOfAvailableHeap());
113113
}
114114

115115
/*
116116
* Search upwards the first two HEAP_STACK_UNTOUCHED_VALUE values after current begin of heap
117+
* If stack uses total heap, we see the current available stack size here :-(
117118
*/
118-
uint16_t getHeapMaxUsedSize() {
119+
int16_t getHeapMaxUsedSize() {
119120
uint8_t *tHeapPtr = getStartOfAvailableHeap();
120121
while (*tHeapPtr != HEAP_STACK_UNTOUCHED_VALUE && *(tHeapPtr + 1) != HEAP_STACK_UNTOUCHED_VALUE && tHeapPtr <= (uint8_t*) SP) {
121122
tHeapPtr++;
122123
}
124+
// Serial.print(F("tHeapPtr=0x"));
125+
// Serial.print((uint16_t)tHeapPtr, HEX);
126+
// Serial.print(F(" SP=0x"));
127+
// Serial.println((uint16_t)SP, HEX);
128+
if ((tHeapPtr-1) == (uint8_t*) SP) {
129+
return -1;
130+
}
123131
// tHeapPtr points now to lowest untouched stack position or to lowest current stack byte
124132
return tHeapPtr - (uint8_t*) __malloc_heap_start;
125133
}
@@ -185,7 +193,7 @@ uint16_t getTheoreticalMaximumAvailableHeapSize(void) {
185193
if (RAMEND <= __malloc_margin) {
186194
return 0;
187195
}
188-
return (RAMEND - RAMSTART) -__malloc_margin; // (128)
196+
return (RAMEND - RAMSTART) - __malloc_margin; // (128)
189197
}
190198

191199
/*
@@ -202,11 +210,11 @@ void printCurrentAvailableHeapSize(Print *aSerial) {
202210
*/
203211
void printCurrentAvailableHeapSizeSimple(Print *aSerial) {
204212
aSerial->print(F("available="));
205-
aSerial->println(SP - (uint16_t) __brkval + 1 - ((uint16_t) __malloc_margin));
213+
aSerial->println(SP - (int16_t) __brkval + 1 - ((int16_t) __malloc_margin));
206214
}
207215

208216
// This define is in AVRUtils.h
209-
//#define PRINT_AVAILABLE_HEAP Serial.print(F("available="));Serial.println(SP - (uint16_t) __brkval + 1 - HEURISTIC_ADDITIONAL_MALLOC_MARGIN - ((uint16_t) __malloc_margin))
217+
//#define PRINT_AVAILABLE_HEAP Serial.print(F("available="));Serial.println(SP - (int16_t) __brkval + 1 - HEURISTIC_ADDITIONAL_MALLOC_MARGIN - ((int16_t) __malloc_margin))
210218

211219
void printBaseRAMData(Print *aSerial) {
212220
// __malloc_heap_end seems to be 0
@@ -259,32 +267,36 @@ void printBaseRAMData(Print *aSerial) {
259267
* Heap available + __malloc_margin (128) = Stack available
260268
* Data+BSS + Heap max used + Stack unused + Stack max used = RAMSIZE
261269
*/
262-
void printRAMInfo(Print *aSerial) {
270+
void printRAMAndStackInfo(Print *aSerial) {
263271

264272
aSerial->print(F("Data+BSS="));
265-
aSerial->print((uint16_t) &__heap_start - RAMSTART);
273+
aSerial->print((int16_t) &__heap_start - RAMSTART);
266274

267275
aSerial->print(F(". Heap: used="));
268-
aSerial->print((uint16_t) getStartOfAvailableHeap() - (uint16_t) &__heap_start);
269-
aSerial->print(F(", max written=")); // if Stack uses total heap, we see the stack size here :-(
276+
aSerial->print((uint16_t) getStartOfAvailableHeap() - (int16_t) &__heap_start);
277+
aSerial->print(F(", max written=")); // If stack uses total heap, we see the stack size here :-(
270278
aSerial->print(getHeapMaxUsedSize());
271279
aSerial->print(F(", max available="));
272-
aSerial->print(RAMEND - (uint16_t) getStartOfAvailableHeap() + 1 - (uint16_t) __malloc_margin);
280+
aSerial->print(RAMEND - (int16_t) getStartOfAvailableHeap() + 1 - (int16_t) __malloc_margin);
273281

274282
aSerial->print(F(". Stack: available="));
275-
aSerial->print(SP - (uint16_t) getStartOfAvailableHeap() + 1);
283+
aSerial->print((SP + 1) - (int16_t) getStartOfAvailableHeap());
276284
aSerial->print(F(", used="));
277285
aSerial->print(RAMEND - SP);
286+
278287
uint16_t tStackUnusedBytes;
279288
aSerial->print(F(", max used="));
280289
aSerial->print(getStackMaxUsedAndUnusedSizes(&tStackUnusedBytes));
281290
aSerial->print(F(", unused="));
282291
aSerial->print(tStackUnusedBytes);
283292
aSerial->print(F(" of current total "));
284-
aSerial->print((RAMEND + 1) - (uint16_t) getStartOfAvailableHeap()); // getStartOfAvailableHeap()
293+
aSerial->print((RAMEND + 1) - (int16_t) getStartOfAvailableHeap()); // getStartOfAvailableHeap()
285294

286295
aSerial->println();
287296
}
297+
void printRAMInfo(Print *aSerial) {
298+
printRAMAndStackInfo(aSerial);
299+
}
288300

289301
/*
290302
* The minimal margin from Heap End to to Stack Start for malloc()
@@ -317,9 +329,9 @@ void testCallocSizesAndPrint(Print *aSerial) {
317329
aSerial->print(F("SP=0x"));
318330
aSerial->print(SP, HEX);
319331
aSerial->print(F(" available="));
320-
aSerial->print(SP - (uint16_t) __brkval + 1 - ((uint16_t) __malloc_margin)- HEURISTIC_ADDITIONAL_MALLOC_MARGIN);
332+
aSerial->print(SP - (int16_t) __brkval + 1 - ((int16_t) __malloc_margin) - HEURISTIC_ADDITIONAL_MALLOC_MARGIN);
321333
aSerial->print(F(" max available="));
322-
aSerial->print(RAMEND - (uint16_t) __brkval + 1 - ((uint16_t) __malloc_margin));
334+
aSerial->print(RAMEND - (int16_t) __brkval + 1 - ((int16_t) __malloc_margin));
323335
uint8_t *tMallocPtr = (uint8_t*) calloc(tMallocSize, 1);
324336

325337
aSerial->print(F(" -> calloc("));
@@ -377,7 +389,7 @@ void initSleep(uint8_t tSleepMode) {
377389
}
378390

379391
/*
380-
* Watchdog wakes CPU periodically and all we have to do is call sleep_cpu();
392+
* Watchdog wakes CPU periodically and all we have to do is call sleep_cpu() - see AVRUtilsDemo
381393
* aWatchdogPrescaler (see wdt.h) can be one of
382394
* WDTO_15MS, 30, 60, 120, 250, WDTO_500MS
383395
* WDTO_1S to WDTO_8S
@@ -399,6 +411,27 @@ void initPeriodicSleepWithWatchdog(uint8_t tSleepMode, uint8_t aWatchdogPrescale
399411
WDTCSR = tWDTCSR; // set final Value
400412
}
401413

414+
/*
415+
* Watchdog interrupts CPU periodically and ISR can be used to handle timeout - see AVRUtilsDemo
416+
* The reason for timeout is NOT removed, i.e. an endless loop will not be exited.
417+
* aWatchdogPrescaler (see wdt.h) can be one of
418+
* WDTO_15MS, 30, 60, 120, 250, WDTO_500MS
419+
* WDTO_1S to WDTO_8S
420+
*/
421+
void initTimeoutWithWatchdog(uint8_t aWatchdogPrescaler) {
422+
MCUSR = ~_BV(WDRF); // Clear WDRF in MCUSR
423+
424+
#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) \
425+
|| defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) \
426+
|| defined(__AVR_ATtiny87__) || defined(__AVR_ATtiny167__)
427+
#define WDTCSR WDTCR
428+
#endif
429+
// Watchdog interrupt enable + reset interrupt flag -> needs ISR(WDT_vect)
430+
uint8_t tWDTCSR = _BV(WDIE) | _BV(WDIF) | (aWatchdogPrescaler & 0x08 ? _WD_PS3_MASK : 0x00) | (aWatchdogPrescaler & 0x07); // handles that the WDP3 bit is in bit 5 of the WDTCSR register,
431+
WDTCSR = _BV(WDCE) | _BV(WDE); // clear lock bit for 4 cycles by writing 1 to WDCE AND WDE
432+
WDTCSR = tWDTCSR; // set final Value
433+
}
434+
402435
/*
403436
* @param aWatchdogPrescaler (see wdt.h) can be one of WDTO_15MS, 30, 60, 120, 250, WDTO_500MS, WDTO_1S to WDTO_8S
404437
* 0 (15 ms) to 3(120 ms), 4 (250 ms) up to 9 (8000 ms)
@@ -455,6 +488,15 @@ void sleepWithWatchdog(uint8_t aWatchdogPrescaler, bool aAdjustMillis) {
455488
}
456489
}
457490

491+
/*
492+
* Sample ISR for Watchdog
493+
* This interrupt prints the timeout message
494+
*/
495+
//ISR(WDT_vect) {
496+
// wdt_reset();
497+
// myLCD.setCursor(0, 3);
498+
// myLCD.print(F("No voltage detected "));
499+
//}
458500
/*
459501
* 0 -> %1
460502
* _BV(CLKPS0) -> %2

src/AVRUtils.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ extern volatile unsigned long timer0_millis;
7070

7171
void initSleep(uint8_t tSleepMode);
7272
void initPeriodicSleepWithWatchdog(uint8_t tSleepMode, uint8_t aWatchdogPrescaler);
73+
void initTimeoutWithWatchdog(uint8_t aWatchdogPrescaler);
7374
uint16_t computeSleepMillis(uint8_t aWatchdogPrescaler);
7475
void sleepWithWatchdog(uint8_t aWatchdogPrescaler, bool aAdjustMillis = false);
7576

@@ -92,11 +93,13 @@ void printCurrentAvailableHeapSizeSimple(Print *aSerial);
9293
void initStackFreeMeasurement();
9394

9495
int16_t getStackMaxUsedAndUnusedSizes(uint16_t *aStackUnusedSizePointer);
96+
int16_t getHeapMaxUsedSize();
9597
void printStackMaxUsedAndUnusedSizes(Print *aSerial);
9698
bool printStackMaxUsedAndUnusedSizesIfChanged(Print *aSerial);
9799

98100
void printBaseRAMData(Print *aSerial);
99-
void printRAMInfo(Print *aSerial);
101+
void printRAMAndStackInfo(Print *aSerial);
102+
void printRAMInfo(Print *aSerial) __attribute__ ((deprecated ("Renamed to printRAMAndStackInfo()")));
100103

101104
bool isAddressInRAM(void *aAddressToCheck);
102105
bool isAddressBelowAvailableHeapStart(void *aAddressToCheck);

0 commit comments

Comments
 (0)