Home > On-Demand Archives > Workshops >

Exception Handling

Nathan Jones - Watch Now - EOC 2025 - Duration: 01:29:14

Exception Handling
Nathan Jones

Programmers often focus on designing the "happy path" for their code—the scenario where everything goes smoothly, values are within range, and no timeouts occur. However, this emphasis can come at the expense of a program's robustness: its ability to continue operating effectively when exceptions or unexpected conditions arise.

In this workshop, participants will actively explore run-time options for handling exceptions, including techniques like global and local error indicators, return values, immutable sections of code, goto chains, and try-catch mechanisms. Through hands-on exercises, you'll learn how to implement these approaches with minimal extra code and complexity.

By the end of this workshop, you'll have practical strategies for building more robust and reliable programs, ensuring that your code can gracefully handle the unexpected while maintaining simplicity and clarity.

M↓ MARKDOWN HELP
italicssurround text with
*asterisks*
boldsurround text with
**two asterisks**
hyperlink
[hyperlink](https://example.com)
or just a bare URL
code
surround text with
`backticks`
strikethroughsurround text with
~~two tilde characters~~
quote
prefix with
>

Stephane.Boucher
Score: 0 | 2 weeks ago | no reply

The chat transcript from the live event has now been posted and is available through the 'Chat Transcript' tab.

SimonSmith
Score: 0 | 2 weeks ago | 1 reply

I found this useful. I think referring to them as "exceptional conditions", or similar would be clearer, as "exception" has a very defined meaning in C++, Python, etc. I hadn't seen the nodiscard and lambda approaches used before. I can imagine the A-B retry very difficult to test works as expected. I think consistency in whatever approach taken is important. A really good example of consistent error handling can be found in the Micrium uC/OS-III source code (an underappreciated RTOS IMHO). Every function has an error output as the last argument, and all possible error enums are summarised in each function header comment.
https://github.com/weston-embedded/uC-OS3

nathancharlesjonesSpeaker
Score: 1 | 2 weeks ago | no reply

I agree with all of this! Thanks for the link to the uC project. Great point, too, that many bugs can lurk in our exception handling code exactly because its often less tested than other parts of the code. It's a great reminder to try to keep those solutions as simple as possible.

Nathan3
Score: 0 | 2 weeks ago | 2 replies

Just watched the replay. Great presentation thanks !
I find that some of the techniques you are showing are great in reducing lines of code but maybe not at code readability or using standards coding rules. I guess it's a tradeoff to find between the two.
As for try / catch, I don't know how it's used on embedded as I've not used C++ for a while but I think that in the general C++ community, use of exception (as it try catch) is rather not recommended these days.
For the use of return values, I quite like the use of POSIX-like return code (0 or >0 for valid, <0 for errors). It's the default way of handling errors in Zephyr RTOS which I've been using for a couple of years now.

nathancharlesjonesSpeaker
Score: 0 | 2 weeks ago | no reply

Actually, if assigning to a variable in a conditional expression is the only problem, then all of those techniques work just fine, provided you don't care after if there was an error or what its value is. E.g.

A() || B() || C();

works just as well as

(err = A()) || (err = B()) || (err = C());

as long as you don't about the value of err after. And if you did, an out-argument could still give that to you:

err_t err = NO_ERR;
A(&err);
B(&err);
C(&err);

like I mentioned in the middle of the workshop.

nathancharlesjonesSpeaker
Score: 0 | 2 weeks ago | no reply

Thanks! I don't disagree that some of my recommendations will be frowned upon by some coding standards. (I'm fairly certain that many will frown on the idea of assigning to a variable like err inside a conditional expression like the logical ors or the if/elses. Are there others you saw?) In that case, I guess you still have the pattern of checking the error variable prior to each block of code:

uint16_t val;
err_t err = A(&val);
if(!err) err = B(val);
if(!err) err = C();
if(err) {...}

Personally, I feel like that amount of added code is exceedingly minimal and, furthermore, the pattern is easy to read once you see that each section of code is just wrapped with if(!err){...} so that the program will skip over them once the err variable is set.
The POSIX-like return codes are fine, provided the function is not also returning a different value through that return value (i.e. it's not an "in-band" error code), as discouraged by SEI ERR02-C.

bboniface
Score: 0 | 2 weeks ago | 2 replies

Hi Nathan, I really appreciated the workshop! Can you expound on the difference/relationship between exception and error? Thanks.

nathancharlesjonesSpeaker
Score: 0 | 2 weeks ago | no reply

I listed several examples of exceptions in the presentation and there were more in the chat.
Examples of errors include:

  • Null pointer dereferencing
  • Dereferencing pointer before memory allocation
  • Improper function arguments
  • Out-of-bounds array index
  • Missing object/file/etc
  • Stack overflow
  • Out of heap space
  • Module called before initialization
  • Critical library return codes (e.g. RTOS unable to register thread, HAL unable to initialize peripheral, etc)
  • Program about to enter an undefined state (e.g. default case in a switch block, invalid next FSM state, etc)
  • Program ROM fails checksum
  • Division by zero
  • The file/stream used by a function is open/closed when it begins/ends executing
  • The value of a const/input-only variable isn't changed by a function
nathancharlesjonesSpeaker
Score: 0 | 2 weeks ago | no reply

Sure thing! I like the way Miro Samek describes it here. He recommends in that article to ask yourself two questions:
(1) "Can a given situation legitimately arise in this particular system?" and
(2) "If it happens, is there anything specific that needs to or can be done in the software?"
If the answer to either question is "Yes", then treat the situation as an exception. Otherwise, it's an error.
If my sensor gives me a weird value I should treat that as an exception; it's totally plausible that a sensor could malfunction. On the other hand, if a function is called with bad parameters (i.e. out-of-bounds array access), I should probably treat that as an error. The best case scenario is that that's the result of a programmer mistake (off-by-one or not checking that the array index was in a valid range before calling the function); in the worst case scenario, though, my program ROM is corrupt or my system is under attack. In those cases, the presence of the error is tantamount to an invalid machine instruction; basically "all bets are off" and the system can't trust anything that happens next so it should probably reset or transition immediately to a safe mode.

Patrick
Score: 0 | 2 weeks ago | 1 reply

I was thinking through the various exception handling methods in terms of Philip Koopman's talk on Embedded System Safety. One of the big reasons I've heard (and thought) to use built-in exceptions handling mechanisms (e.g., C++ throw/try/catch) is because it separates exceptional conditions from the actual logic of the code. In other words, the code you "care about the majority of the time" is not obfuscated with a bunch of exception handling code. But, thinking from a safety perspective, I'm wondering if that's actually such a good thing? I would imagine it is harder to prove (e.g., in formal reviews) that safety was taken into consideration when exceptions are "bubbled up" the stack with no visible code to show that. At least with explicit exception handling code, you can prove what you are doing for safety. Thoughts?

nathancharlesjonesSpeaker
Score: 0 | 2 weeks ago | no reply

Ooh, there are two great points to which I'd like to respond here.
First, you're absolutely right that exceptions make code verification difficult. A major criticism of try/catch exceptions is that they amount to structured gotos, leaving a nearly unknowable number of possible execution paths in your code base (see here). And because of that, it can be very hard to ensure that your code remains correct and free of memory leaks or other errors when using try/catch exceptions (see here). A quote from "Code Complete" (by Steve McConnell) that I almost included in my presentation is, "Several programming languages have supported exceptions for 5-10 years or more, but little conventional wisdom has emerged about how to use them safely."
Second, I think that moving the exception handling code to a catch block seems like a neat idea, but it actually complicates things in practice. For instance, you'd need multiple catch blocks and multiple different types of thrown exceptions if there's any sort of nuance to your program's exception handling (e.g. "if A() returns exception 1 we can retry up to 3 times but if it returns exception 2 then we need to default to an alternate operation and if B() returns any exception then we need to trigger this other operation and..."). In the simplest case where your program does exactly one thing any time there's any exception in the try block, then it can possibly be easier to read. But beyond that, I just think it doesn't scale well.

Patrick
Score: 0 | 2 weeks ago | 1 reply

Excellent session Nathan! Thank You! I really enjoyed the interaction, getting to hear everyone's experiences and ideas.

nathancharlesjonesSpeaker
Score: 0 | 2 weeks ago | no reply

Thanks so much!

Tommy
Score: 0 | 2 weeks ago | 1 reply

Great session!

nathancharlesjonesSpeaker
Score: 0 | 2 weeks ago | no reply

Thank you! I hope it was useful.

MarkB
Score: 1 | 2 weeks ago | no reply

Perhaps the "Fail Quiet" concept Phillip discussed can be worked into exception handling thinking, in a safety context. Or maybe it's orthogonal. Not sure.
The opposite concept to exception handling is assertion testing (with "typed" assertions). If asserting a condition we "hope for" goes beyond just doing nothing when all is okay, but instead we post a successful assertion to a queue that is being monitored by another agent (say a watchdog thread or a safety thread), then the absence of an assertion succeeding will trigger a condition that needs to be noticed and responded too.

11:03:49 From frank to Everyone:
	Hi this is Frank from Germany
	
11:03:52 From Daniel to Everyone:
	Greetings from NC!
	
11:03:54 From brian to Everyone:
	This is Brian from Ann Arbor, MI.
	
11:03:55 From Tom to Everyone:
	Tom Propst from Ft Collins, CO, USA
	
11:03:55 From Otzen to Everyone:
	I'm from Denamrk, and a dad of four ;-)
	
11:03:59 From maria to Everyone:
	Hi Maria from Bolivia
	
11:04:01 From Luke Hoffman to Everyone:
	Pennsylvania, USA.
	
11:04:02 From BanksG02 to Everyone:
	I'm the firmware developer in a small development team in the UK.
	
11:04:07 From Esteban to Everyone:
	Greetings from Spain
	
11:04:07 From garyb to Everyone:
	Sunny & warm southern Minn. Retired embedded FW developer.
	
11:04:06 From Patrick Wright to Everyone:
	This is Patrick from St. Charles, Illinois, USA
	
11:04:07 From Suzi to Everyone:
	Hi from Eugene Oregon!
	
11:04:16 From Vitor Woyakewicz to Everyone:
	Hit, this is Vitor From Brazil
	
11:04:35 From Mark to Everyone:
	This is Mark from Indiana. Hello!
	
11:04:35 From Felipe Robaina to Everyone:
	Hi from Uruguay!
	
11:04:49 From Greg Burk to Everyone:
	Hello from MInnesota
	
11:04:53 From ts to Everyone:
	I’m from Southern Germany, actually watching this on a campground in East-France. Hi everybody!
	
11:04:55 From Oliver to Everyone:
	Hi, here is Oliver from Germany.
	
11:05:06 From Ingo to Everyone:
	Hi from Frankfurt Germany
	
11:05:58 From Viktor to Everyone:
	Hello from Lille, France !
	
11:06:16 From Daniel to Everyone:
	😂 classic joke
	
11:08:08 From Keith J to Everyone:
	Hello from Wisconsin
	
11:09:11 From Oleg Zvonarov (Comcast) to Everyone:
	Hello from Philadelphia!
	
11:11:59 From BanksG02 to Everyone:
	Network timeout
	
11:12:32 From Keith J to Everyone:
	exception: underflowing a ring buffer needed for serial audio interface
	
11:12:33 From ts to Everyone:
	Lost WIFI connection while trying to transfer data.
	
11:12:34 From garyb to Everyone:
	16 bit timer future event overflow not handled causing hours long delay.
	
11:12:47 From Viktor to Everyone:
	Hardware device gets disconnected
	
11:12:51 From BanksG02 to Everyone:
	Peripheral doesn't respond.
	
11:12:55 From Tom to Everyone:
	Value limits
	
11:12:58 From Otzen to Everyone:
	fputs not delivering all of the string
	
11:13:00 From brian to Everyone:
	Signal saturation
	
11:13:13 From garyb to Everyone:
	Noise corruption of UART sync causing buffer corruption.
	
11:13:21 From BanksG02 to Everyone:
	No GPS fix.
	
11:13:26 From Keith J to Everyone:
	that's a good one: peripheral doesn't response either because of a failure or removed if removeable
	
11:13:47 From BanksG02 to Everyone:
	Buffer full.
	
11:13:58 From Prasanna to Everyone:
	Read/write error?
	
11:14:21 From BanksG02 to Everyone:
	CRC mismatch.
	
11:14:57 From Otzen to Everyone:
	fast-task not done before next IRQ
	
11:15:30 From Daniel to Everyone:
	Time register overflow results in negative time!
	
11:15:50 From frank to Everyone:
	IRQ load higher than expected
	
11:16:06 From Vitor Woyakewicz to Everyone:
	DMA between MCU and Processor, one side isnt fast enough to read/write in the memory
	
11:16:07 From Gonzalo to Everyone:
	That infrared receptor Will receive signals form other remote controllers
	
11:16:08 From Otzen to Everyone:
	data not ready, so something down the chain just defaultet to zero...
	
11:16:36 From Mark to Everyone:
	An intermittent break on the power supplied to the sensor.
	
11:16:46 From Patrick Wright to Everyone:
	Brownout conditions causing invalid values from hardware to firmware
	
11:18:45 From Otzen to Everyone:
	Replying to "data not ready, so something down the chain just d...":
	And the zero did not create exception, but caused a filter to suddently having INF value.
	
11:22:10 From k chaloupka to Everyone:
	what are your preferences in defining return values?
	
11:23:42 From Otzen to Everyone:
	oposite tradition i prefer functions to return true on success,
	
11:24:06 From Mark to Everyone:
	Another error report strategy extending the return value concept:
	Return values can return an optional (Swift), or Result that Rust uses.
	
11:25:22 From frank to Everyone:
	Replying to "oposite tradition i prefer functions to return tru...":
	Doesn't that end up in deeply nested code?
	
11:26:28 From Luke_O to Everyone:
	Replying to "oposite tradition i prefer functions to return tru...":
	main issue I have with this is it can lead to deep nesting.
	
11:26:47 From Mark to Everyone:
	Aha
	
11:27:47 From elaine.bosman to Everyone:
	Replying to "oposite tradition i prefer functions to return tru...":
	you could do this to prevent the nesting, but it introduces more exit points to the function. which can be annoying to debug
	
	if (foo() == false){
	 // handle
	}
	// rest of function
	
11:27:50 From Otzen to Everyone:
	Replying to "oposite tradition i prefer functions to return tru...":
	it can if you don't care.
	but if you have many steps in same function, put the return in a variable and use that to decide if you continiue.
	
11:30:34 From Otzen to Everyone:
	Replying to "oposite tradition i prefer functions to return tru...":
	success = foo();
	if(success)
	{ success = bar()};
	if(success)
	{ success = zoo()};
	...
	
	But it is not often I have such long chains in one function.
	
11:32:14 From Mark to Everyone:
	Wish my IDE could do yellow highlighting for comments with ASCII art in them 🙂
	
11:33:47 From Gonzalo to Everyone:
	For me in my recent project what has worked well for me is kind of a bit of TDD: creating first the infrastructure to report exceptions, then programming my real functionality. It is incredible how helpful is to have  that readable report of exception we think will not happen but do so often
	
11:33:56 From Tom to Everyone:
	@Nathan Jones can you paste the links here?
	
11:34:29 From Otzen to Everyone:
	The last bad exception I saw was in a python program, where the author did a catch all, and assumed only one errror could be the cause, and just reported that.
	
11:34:32 From Mark to Everyone:
	https://wokwi.com/projects/430777548539551745
	
11:34:36 From Nathan Jones to Everyone:
	https://wokwi.com/projects/430777548539551745
	
11:34:42 From Nathan Jones to Everyone:
	https://wokwi.com/projects/430842301912312833
	
11:35:07 From Nathan Jones to Everyone:
	https://www.tinkercad.com/things/7TYqSek80uE-eoc2025exception-handling-workshoptinkercad?sharecode=89255xc2210w49tafGJeKD75uj5ZI1LUb-5E1cqrbjE
	
11:35:16 From Devidas to Everyone:
	We have prepare list of error code and depending on function and condition , function return error code
	
11:35:25 From Patrick Wright to Everyone:
	std::expected (or etl::expected for older c++ versions) has been something I've tried to use more often.  Especially because you can still use it in a clean way with functions that return void.  The return value then says "I expect to return void, but if an exception occurs, I return (for example) an Enum error code instead"
	
11:36:34 From Mark to Everyone:
	Replying to "The last bad exception I saw was in a python progr...":
	Sort of like an idiot light on a car instrument panel
	
11:37:59 From Suzi to Everyone:
	can we have instructions in the chat as well?
	
11:38:29 From Nathan Jones to Everyone:
	Sure! Instructions for the exercises, you mean?
	
11:38:45 From Suzi to Everyone:
	Yes!
	
11:39:34 From Nathan Jones to Everyone:
	For this exercise, I want you to pick a function from one of your projects or from the list of samples I provided.
	
11:39:57 From Nathan Jones to Everyone:
	Then, identify what possible exceptions it might need to address. Put those in a comment block in the function.
	
11:40:16 From Suzi to Everyone:
	Thank you!
	
11:40:19 From Nathan Jones to Everyone:
	Last, alter the function signature so that it could possible report an error in the future.
	
11:41:41 From Mark to Everyone:
	Replying to "For me in my recent project what has worked well f...":
	It would be cool to have TDD and exception handling integrated somewhat, so that a small subset of the unit tests would actually execute at runtime and handle a test failure as an exception.
	
11:42:47 From BanksG02 to Everyone:
	err_t readNTCTemperature(float *temp) {
	  err_t result = NO_ERROR;
	  int adc = analogRead(ntcPin);
	  float resistance = (1023.0 / adc) - 1.0;
	  float temperature;
	  resistance = 10000.0 / resistance; // Assuming a 10k NTC at 25°C
	  
	  if (resistance > UPPER_BOUND || resistance < LOWER_BOUND)
	  {
	    result = OUT_OF_BOUNDS;
	  }
	
	  if (NO_ERROR == result)
	  {
	    float temperature = 1.0 / (log(resistance / 10000.0) / 3950.0 + 1.0 / 298.15) - 273.15;
	    if (temperature > TEMP_UPPER_BOUND || temperature < TEMP_LOWER_BOUND)
	    {
	      result = TEMP_OUT_OF_BOUNDS
	    }
	  }
	  
	  if (NO_ERROR == result)
	  {
	    *temp = temperature;
	  }
	  
	}
	
11:43:23 From BanksG02 to Everyone:
	🎉
	
11:43:32 From Luke_O to Everyone:
	Replying to "err_t readNTCTemperature(float *temp) {   err_t re...":
	don't forget the possible divide by zero with adc
	
11:44:02 From brian to Everyone:
	Replying to "err_t readNTCTemperature(float *temp) {   err_t re...":
	don't forget to return the result
	
11:44:14 From Eric Habets to Everyone:
	and log(0) is also forbidden
	
11:45:50 From Angel Suazo to Everyone:
	Link 01:
	~~~~~~~~~~~~~~
	void updateEEPROM(float t, float ax, float ay, float az) {
	  int addr = EEPROM_START + (recordIndex * RECORD_SIZE);
	  EEPROM.put(addr, t);
	  EEPROM.put(addr + sizeof(float), ax);
	  EEPROM.put(addr + 2 * sizeof(float), ay);
	  EEPROM.put(addr + 3 * sizeof(float), az);
	  recordIndex = (recordIndex + 1) % MAX_RECORDS;
	}
	
	It would be nice to put some EEPROM Write Failures Exceptions and address Overflow. To know when the write operation fails and ensure the calculated address is below the EEPROM size.
	
11:45:52 From Otzen to Everyone:
	Replying to "err_t readNTCTemperature(float *temp) {   err_t re...":
	It's not in the doc, but if you look in the source the analogRead() will rteturn -1 in case of bad config.
	
11:46:12 From Mark to Everyone:
	void printStoredData() {
	  dataFile = SD.open("/data.txt");
	  if (dataFile) {
	  // Add exception handling to the case where data file is not available for more than 100 msec
	    while (dataFile.available()) {
	      Serial.write(dataFile.read());
	    }
	    dataFile.close();
	    } else {
	      Serial.println("Failed to open data file.");
	    }
	}
	
11:47:02 From garyb to Everyone:
	I entered ? for threshold and got a zero } else if (input.startsWith("threshold ")) {
	      /* need to validate if threshold a number in range, not missing parameter or unexpected character */
	      motionThreshold = input.substring(10).toFloat();
	
11:50:37 From Otzen to Everyone:
	For serious exceptions, I often put a soft break symbol, great help if you are debugging, and the symbol is ignored by the CPU if the debugger is not connected.
	
11:51:27 From Patrick Wright to Everyone:
	Replying to "For serious exceptions, I often put a soft break s...":
	At the highest level, I add one of these "soft break points" to my assert handler.
	
11:52:09 From Eric Habets to Everyone:
	recordIndex = (recordIndex + 1) % MAX_RECORDS; is also wrong if recordIndex overflows as MAX_RECORDS is 10.
	
11:52:29 From garyb to Everyone:
	For very limited or deep embedded system an exception handling routine to indicate the stack pointer address (LED blink if necessary) where the error originated - useful for memfault type calls to invalid memory.
	
11:57:16 From Patrick Wright to Everyone:
	Aren't both of these methods (goto and case) similar to what the C++ compiler will generate for exceptions?
	
11:58:05 From garyb to Everyone:
	Intentional switch fall through should have a comment to indicate to next developer this is intended behavior!
	
11:58:36 From Patrick Wright to Everyone:
	Replying to "Intentional switch fall through should have a comm...":
	I think C++ helps you here with [[fallthrough]]
	
11:58:59 From Mark to Everyone:
	Replying to "Aren't both of these methods (goto and case) simil...":
	Yes, like the finally{} clause.
	
12:02:34 From Nathan Jones to Everyone:
	Replying to "Intentional switch fall through should have a comm...":
	Yes, you are correct! I knew that and left if off for brevity. Shame on me!
	
12:03:56 From elaine.bosman to Everyone:
	err_t saveToSD(float t, float ax, float ay, float az) {
	  dataFile = SD.open("/data.txt", FILE_APPEND);
	  err_t result = ERR_OK;
	  if (dataFile) {
	    int16_t written = dataFile.printf("%.2f,%.2f,%.2f,%.2f\n", t, ax, ay, az);
	    dataFile.close();
	    if (written < 0)
	    {
	      result = ERR_SD_WRITE_FAIL;
	    }
	  }
	  else
	  {
	    result = ERR_SD_FILE_OPEN_FAIL;
	  }
	  return result;
	}
	
12:05:47 From BanksG02 to Everyone:
	I maybe went too far before, putting exception detecting code in - and forgot to do some stuff. Here's an updated version...
	
	err_t readNTCTemperature(float *temp) {
	  err_t result = NO_ERROR;
	  int adc = analogRead(ntcPin);
	  float resistance = (1023.0 / adc) - 1.0;
	  float temperature;
	  resistance = 10000.0 / resistance; // Assuming a 10k NTC at 25°C
	  
	  if (0 <= resistance)
	  {
	    result = SENSOR_FAILURE;
	  }
	  else if (resistance > UPPER_BOUND || resistance < LOWER_BOUND)
	  {
	    result = OUT_OF_BOUNDS;
	  }
	
	  if (NO_ERROR == result)
	  {
	    /* Case where resistance is 0 has been accounted for */
	    float temperature = 1.0 / (log(resistance / 10000.0) / 3950.0 + 1.0 / 298.15) - 273.15;
	    if (temperature > TEMP_UPPER_BOUND || temperature < TEMP_LOWER_BOUND)
	    {
	      result = TEMP_OUT_OF_BOUNDS
	    }
	  }
	  
	  if (NO_ERROR == result)
	  {
	    *temp = temperature;
	  }
	  
	  return result;
	}
	
12:07:39 From BanksG02 to Everyone:
	To avoid deeply nested if statements where there are many things to check, I like to simply check the error code at the outermost level at each stage before proceeding as above.
	
12:08:49 From Patrick Wright to Everyone:
	std::expected readNTCTemperature()
	{
	  //What if analogRead fails, returns bad value?
	  //What if analogRead hangs (ADC loop error)?
	  int adc = analogRead(ntcPin);
	  if (adc <= 0) return std::unexpected(Error::ANALOG_READ_FAILED);
	
	  //What if adc == 0? -> Divide-by-zero exception
	  float resistance = (1023.0 / adc) - 1.0;
	  if (resistance <= 0) return std::unexpected(Error::SATURATED);
	
	  //What if resistance == 0 (happens if adc == 1023.0)
	  resistance = 10000.0 / resistance; // Assuming a 10k NTC at 25°C
	
	  //What if resistance == 0? -> log(0) is undefined
	  float temperature = 1.0 / (log(resistance / 10000.0) / 3950.0 + 1.0 / 298.15) - 273.15;
	
	  return temperature;
	}
	
12:10:20 From garyb to Everyone:
	Error behavior really needs to have concept of safety behavior (Phil Koopman's presentation). Events really need to know/understand the what if a fault occurs.
	
12:10:44 From Eric Habets to Everyone:
	err_t connectWiFi() {
	  WiFi.begin(ssid, password);
	  Serial.print("Connecting to WiFi");
	  while (WiFi.status() != WL_CONNECTED) {
	    delay(500); Serial.print(".");
	  }
	  if (WL_CONNECTED != WiFi.status()){
	     return ERROR;
	  }
	  Serial.println("\nWiFi connected.");
	  return NOERROR;
	}
	
12:10:51 From Patrick Wright to Everyone:
	Replying to "Error behavior really needs to have concept of saf...":
	I agree.  I've noticed how related these two concepts are.
	
12:13:41 From Tom to Everyone:
	Replying to "To avoid deeply nested if statements where there a...":
	We use a macro from our `error` module that looks like:
	`IF_NO_ERROR(err_val, func(...));`
	That macro will only call `func` if `err_val` is zero. If it does call `func`, it assigns the return value to `err_val`.
	
12:13:44 From Otzen to Everyone:
	Replying to "Error behavior really needs to have concept of saf...":
	Agree, the overall design often forget to describe how to fail.
	I often find myself in the situation, that i see a possible exception, but there is no proper way to fail. So I am often left with either ignore the error and hope for the best, or trigger a reset of the system.
	
12:14:36 From Otzen to Everyone:
	Replying to "To avoid deeply nested if statements where there a...":
	A macro'fied version of the code i posted earlier ;-)
	
12:16:44 From garyb to Everyone:
	Some code/style standards don't like Ternary.
	
12:19:21 From Mark to Everyone:
	Java, C#, Python have finally{} clause (but not C++) that can handle clean-up after that sequence of try/catch…
	
12:20:09 From Patrick Wright to Everyone:
	Replying to "Java, C#, Python have finally{} clause (but not C+...":
	I think the idea would be to use destructors to have the compiler automatically clean-up what is necessary depending on the context (i.e., how "deep" you got into the execution of the function).
	
12:21:04 From Mark to Everyone:
	Replying to "Java, C#, Python have finally{} clause (but not C+...":
	Yeah, I guess that’s why C++ does not need finally.
	
12:22:02 From Patrick Wright to Everyone:
	Replying to "Java, C#, Python have finally{} clause (but not C+...":
	The nomenclature they prefer is RAII (Resource Allocation Is Initialization)
	
12:25:02 From Patrick Wright to Everyone:
	The checking of the out-argument in each subsequent function call remines me of how LabVIEW programs typically handle exceptions.  You keep "flowing" the error "structure" into each VI and each VI checks to see if an error is already set.  If yes, then it does nothing and passing the error along.
	
12:25:12 From brian to Everyone:
	@Nathan Jones, can the example while(1) function call sequence (A, B, C) and other loop logic alternatively be arranged into a state machine switch block?
	
12:26:27 From Patrick Wright to Everyone:
	And for those without C++23, ETL (Embedded Template Library) provides an implementation: https://etlcpp.com/expected.html
	
12:26:35 From Mark to Everyone:
	Replying to "Java, C#, Python have finally{} clause (but not C+...":
	I like that 🙂.
	
12:31:52 From Nathan Jones to Everyone:
	Abort exercise!:
	1) Pick a task or loop from the project you've been working on
	2) Use the error code from the function you updated earlier to "abort" that task/loop when an error occurs
	
12:32:16 From Mark to Everyone:
	Replying to "And for those without C++23, ETL (Embedded Templat...":
	Thanks for the link. Makes me want to start using C++ again.
	
12:35:37 From Patrick Wright to Everyone:
	I'm not sure if this is legit Arduino code, but I think this is an option using std::expected:
	
	void handleTemperature(float temperature)
	{
	  displayLCD(temperature, a.acceleration.x, a.acceleration.y, a.acceleration.z);
	  updateEEPROM(temperature, a.acceleration.x, a.acceleration.y, a.acceleration.z);
	  setServoFromTemp(temperature);
	}
	
	void handleTemperatureError(Error err)
	{
	  Serial.println("Error reading temperature: %u", err);
	}
	
	void loop()
	{
	  readNTCTemperature()
	  .and_then(handleTemperature)
	  .or_else(handleTemperatureError);
	
	  mpu.getEvent(&a, &g, &temp);
	  if (motionDetected) {
	    digitalWrite(ledPin, HIGH);
	    delay(1000);
	    digitalWrite(ledPin, LOW);
	    motionDetected = false;
	  }
	
	  delay(2000); // Sample every 2 seconds
	}
	
12:35:47 From Nathan Jones to Everyone:
	Replying to "@Nathan Jones, can the example while(1) function c...":
	Definitely! We'll actually discuss FSMs at the end of the workshop. My only concern would be the amount of added code and whether the extra effort would end up being worth it. In this example it's probably not worth it (all we're doing is moving on to the next function or aborting); but if the flowchart for my function was much more complicated ("run A() 3 times but if the last error code isn't a timeout then run altA() twice and then..."), then an FSM could make a lot of sense.
	
12:35:54 From BanksG02 to Everyone:
	I know it's long winded and not very elegant looking, but it is very easy to read.
	
	void loop() {
	  err_t error = readNTCTemperature(&temperature);
	
	  if (NO_ERROR == error)
	  {
	    error = mpu.getEvent(&a, &g, &temp);
	  }
	  
	  if (NO_ERROR == error)
	  {
	    error = displayLCD(temperature, a.acceleration.x, a.acceleration.y, a.acceleration.z);
	  }
	  
	  if (NO_ERROR == error)
	  {
	    error = updateEEPROM(temperature, a.acceleration.x, a.acceleration.y, a.acceleration.z);
	  }
	
	  if (NO_ERROR == error)
	  {
	    error = setServoFromTemp(temperature);
	  }
	
	  if ((NO_ERROR  == error) && motionDetected) {
	    digitalWrite(ledPin, HIGH);
	    delay(1000);
	    digitalWrite(ledPin, LOW);
	    motionDetected = false;
	  }
	
	  delay(2000); // Sample every 2 seconds
	}
	
12:36:55 From Patrick Wright to Everyone:
	This is just a general comment for everyone, but I discovered Zoom has an option to add a code snippet if you click the "Format" button (looks like a pencil with the letter T).
	
12:37:33 From Nathan Jones to Everyone:
	Replying to "I know it's long winded and not very elegant looki...":
	I like it! The regular structure makes it very easy to understand.
	
12:38:23 From Eric Habets to Everyone:
	millis() is 32 bit only and overflows after 49.7 days … not good for embedded.
	
12:42:23 From garyb to Everyone:
	Example 6b needs to account real time impact of retries - can you make schedules?
	
12:43:42 From Mark to Everyone:
	Replying to "enum class Error {   ANALOG_READ_FAILED   SATURATE...":
	How did you paste this into the chat with the rounded rectangle?
	
12:44:27 From Patrick Wright to Everyone:
	Replying to "enum class Error {   ANALOG_READ_FAILED   SATURATE...":
	{99BD873F-E7F0-4C4B-BDBC-36C1211B72C8}.png
	
12:44:40 From Mark to Everyone:
	Replying to "enum class Error {   ANALOG_READ_FAILED   SATURATE...":
	Thanks!
	
12:45:29 From Luke Hoffman to Everyone:
	Have to leave, thanks for the workshop!
	
12:46:38 From brian to Everyone:
	Replying to "@Nathan Jones, can the example while(1) function c...":
	/* Using a state machine method */
	while (1) {
	    static int val;
	    switch state {
	        case INIT:
	            /* initial logic if necessary */
	            state = STATE_1;
	            break;
	        case STATE_1:
	            err = A(int &val);
	            if err {
	                state = ERR;
	            } else {
	                state = STATE_2;
	            }
	            break;
	        case STATE_2:
	            err = B(val);
	            if err {
	                state = ERR;
	            } else {
	                state = STATE_3;
	            }
	        ...
	        case STATE_n:
	            /* post functional sequence logic */
	            break;
	        case ERR:
	        default:
	            state = ERR;
	    }
	    /* handle errors */
	}
	
12:48:24 From Mark to Everyone:
	int void printStoredData() {
	  int timeout = 1000;
	  dataFile = SD.open("/data.txt");
	  if (dataFile) {
	    while (true)
	    {
	      if (dataFile.available()) {
	        Serial.write(dataFile.read());
	        break;
	      }
	      else {
	        timeout--;
	        if (timeout == 0) {
	          return 1
	        }
	      }
	    }
	    dataFile.close();
	  } else {
	    Serial.println("Failed to open data file.");
	  }
	  return 0
	}
	
	…
	
12:49:13 From Mark to Everyone:
	Replying to "int void printStoredData() {   int timeout = 1000;...":
	void handleSerialInput() {
	  if (Serial.available()) {
	    String input = Serial.readStringUntil('\n');
	    input.trim();
	    if (input.startsWith("interval ")) {
	      sampleInterval = input.substring(9).toInt() * 1000;
	      Serial.printf("Sampling interval set to %lu ms\n", sampleInterval);
	    } else if (input.startsWith("threshold ")) {
	      motionThreshold = input.substring(10).toFloat();
	      Serial.printf("Motion threshold set to %.2f m/s^2\n", motionThreshold);
	    } else if (input == "print") {
	      if 0 != printStoredData() {
	        print("ERROR #123")
	      }
	    } else if (input == "help") {
	      Serial.println("Commands:");
	      Serial.println("  interval   - Set sample interval");
	      Serial.println("  threshold    - Set motion threshold");
	      Serial.println("  print               - Print stored data");
	      Serial.println("  help                - Show commands");
	    } else {
	      Serial.println("Unknown command. Type 'help'.");
	    }
	  }
	}
	
12:50:08 From Mark to Everyone:
	Great workshop, Nathan. Thanks!
	
12:50:13 From Lyden Smith to Everyone:
	Thanks Nathan!
	
12:50:16 From Eric Habets to Everyone:
	Thank you Nathan!
	
12:50:20 From garyb to Everyone:
	Thanks Nathan.
	
12:50:21 From frank to Everyone:
	Thx for the presenstation
	
12:50:23 From Angel Suazo to Everyone:
	Thank you
	
12:50:27 From Andrew to Everyone:
	thank you
	
12:50:33 From Esteban to Everyone:
	Thanks!
	
12:50:37 From Immanuel to Everyone:
	Thank you very much. Very interesting!
	
12:50:39 From Otzen to Everyone:
	the ... at top of chat, click and select "save"
	
12:50:44 From Angel Suazo to Everyone:
	Very good workshop
	
12:51:04 From Raul Pando to Everyone:
	Thanks Nathan!
	
12:51:28 From Keith J to Everyone:
	Thanks Nathan
	
12:51:29 From Oleg Zvonarov to Everyone:
	Thank you Nathan!
	
12:54:10 From Erik to Everyone:
	
	Thanks !
	
12:56:20 From Patrick Wright to Everyone:
	If you can find a solution to use C++ exceptions well in limited systems, that would be a happy day!
	
12:56:54 From garyb to Everyone:
	How critical is error is a factor in the log decision.
	
12:57:16 From Patrick Wright to Everyone:
	One of the concepts that helped my was "Design By Contract" where you do as you said an assert as often as possible to catch those things that are truly errors (not exceptions).
	
12:59:11 From garyb to Everyone:
	Limited uC resource is one of the real challenged for the developer.
	
13:02:10 From Mark to Everyone:
	Replying to "Limited uC resource is one of the real challenges ...":
	Yeah, That’s what makes embedded so much fun!
	Like creatively solving puzzles that are not trivial.
	
13:02:33 From garyb to Everyone:
	Replying to "Limited uC resource is one of the real challenges ...":
	All in a day's work!
	
13:02:45 From Keith J to Everyone:
	Great seminar.  Thanks Nathan
	
13:02:55 From brian to Everyone:
	Replying to "@Nathan Jones, can the example while(1) function c...":
	@Nathan Jones, can you explain further what would the downsides be of using an FSM even for this simple exercise?
	
13:02:54 From Stephane to Everyone:
	Thank you Nathan!
	
13:03:09 From Nathan Jones to Everyone:
	Replying to "One of the concepts that helped my was "Design By ...":
	Definitely! I also had the thought in preparing my notes that the idea of an exception seems to overlap quite a bit with the idea of a post-condition being invalid.
	
13:04:45 From BanksG02 to Everyone:
	Thank you. Very engaging workshop.
	
13:05:01 From RF to Everyone:
	Very well composed with excellent delivery, well done. Certainly ‘food for thought’ on an oft neglected important domain.
	
13:05:31 From Suzi to Everyone:
	Thank you! Great talk!
	
13:05:34 From Eric Habets to Everyone:
	@brain: is there an exit from the while loop missing?
	
13:05:37 From garyb to Everyone:
	Replying to "Limited uC resource is one of the real challenges ...":
	All of that and in the heat of the schedule so easy to just make it work - fix later too often only happens when the customer reports an issue - and debugging/replicating is very painful.
	
13:05:58 From Nathan Jones to Everyone:
	Replying to "@Nathan Jones, can the example while(1) function c...":
	Just the verbosity. It's not much in this example (we went from a 3-5 line program to a ~20 line program), but I could imagine it getting quite large. If your code there makes sense for you, though, then do it! The upside to that code is that if your exception handling gets any more complex in the future, you already have the scaffolding to add or edit states and easily implement that more complex behavior.
	
13:06:18 From Otzen to Everyone:
	99% of codestandards forbids "goto". Maybe we could loosen that a bit by saying "goto only allowed to jum forward in code" and only within same function (don't remember if C allows goto ot of function)
	
13:06:30 From brian to Everyone:
	Replying to "@brian: is there an exit from the while loop missi...":
	If I recall correctly, the exercise was a while (1) loop.
	

OUR SPONSORS & PARTNERS