1010
1111#include "lib/fatfs/ff.h"
1212#include "lib/fatfs/diskio.h"
13+ #include "lib/mp-readline/readline.h"
1314#include "lib/utils/pyexec.h"
1415#include "extmod/fsusermount.h"
1516
2324
2425#include "autoreset.h"
2526#include "mpconfigboard.h"
26- #include "neopixel_status .h"
27+ #include "rgb_led_status .h"
2728#include "tick.h"
2829
2930fs_user_mount_t fs_user_mount_flash ;
@@ -131,7 +132,7 @@ static char *stack_top;
131132static char heap [16384 ];
132133
133134void reset_mp (void ) {
134- new_status_color (0x8f , 0x00 , 0x8f );
135+ new_status_color (0x8f008f );
135136 autoreset_stop ();
136137 autoreset_enable ();
137138
@@ -181,7 +182,7 @@ void reset_samd21(void) {
181182 system_pinmux_group_set_config (& (PORT -> Group [1 ]), pin_mask [1 ] & ~MICROPY_PORT_B , & config );
182183}
183184
184- bool maybe_run (const char * filename , int * ret ) {
185+ bool maybe_run (const char * filename , pyexec_result_t * exec_result ) {
185186 FILINFO fno ;
186187#if _USE_LFN
187188 fno .lfname = NULL ;
@@ -193,32 +194,171 @@ bool maybe_run(const char* filename, int* ret) {
193194 }
194195 mp_hal_stdout_tx_str (filename );
195196 mp_hal_stdout_tx_str (" output:\r\n" );
196- * ret = pyexec_file (filename );
197+ pyexec_file (filename , exec_result );
197198 return true;
198199}
199200
200- void start_mp (void ) {
201+ bool start_mp (void ) {
202+ bool cdc_enabled_at_start = mp_cdc_enabled ;
201203 #ifdef AUTORESET_DELAY_MS
204+ if (cdc_enabled_at_start ) {
202205 mp_hal_stdout_tx_str ("\r\n" );
203206 mp_hal_stdout_tx_str ("Auto-soft reset is on. Simply save files over USB to run them.\r\n" );
204- mp_hal_stdout_tx_str ( "Type anything into the REPL to disable and manually reset (CTRL-D) to re-enable.\r\n" );
207+ }
205208 #endif
206209
207- new_status_color (0x00 , 0x00 , 0x8f );
208- int ret = 0 ;
209- bool found_boot = maybe_run ("settings.txt" , & ret ) ||
210- maybe_run ("settings.py" , & ret ) ||
211- maybe_run ("boot.py" , & ret ) ||
212- maybe_run ("boot.txt" , & ret );
213- if (found_boot && ret & PYEXEC_FORCED_EXIT ) {
214- return ;
210+ new_status_color (BOOT_RUNNING );
211+ pyexec_result_t result ;
212+ bool found_boot = maybe_run ("settings.txt" , & result ) ||
213+ maybe_run ("settings.py" , & result ) ||
214+ maybe_run ("boot.py" , & result ) ||
215+ maybe_run ("boot.txt" , & result );
216+ bool found_main = false;
217+ if (!found_boot || !(result .return_code & PYEXEC_FORCED_EXIT )) {
218+ new_status_color (MAIN_RUNNING );
219+ found_main = maybe_run ("code.txt" , & result ) ||
220+ maybe_run ("code.py" , & result ) ||
221+ maybe_run ("main.py" , & result ) ||
222+ maybe_run ("main.txt" , & result );
223+ }
224+
225+ if (result .return_code & PYEXEC_FORCED_EXIT ) {
226+ return reset_next_character ;
215227 }
216228
217- new_status_color (0x00 , 0x8f , 0x00 );
218- maybe_run ("code.txt" , & ret ) ||
219- maybe_run ("code.py" , & ret ) ||
220- maybe_run ("main.py" , & ret ) ||
221- maybe_run ("main.txt" , & ret );
229+ // If not is USB mode then do not skip the repl.
230+ #ifndef USB_REPL
231+ return false;
232+ #endif
233+
234+ // Wait for connection or character.
235+ new_status_color (ALL_DONE );
236+ bool cdc_enabled_before = false;
237+ uint32_t pattern_start = ticks_ms ;
238+
239+ printf ("result code %d %d.\r\n" , result .return_code , result .exception_line );
240+ uint32_t total_exception_cycle = 0 ;
241+ uint8_t ones = result .exception_line % 10 ;
242+ ones += ones > 0 ? 1 : 0 ;
243+ uint8_t tens = (result .exception_line / 10 ) % 10 ;
244+ tens += tens > 0 ? 1 : 0 ;
245+ uint8_t hundreds = (result .exception_line / 100 ) % 10 ;
246+ hundreds += hundreds > 0 ? 1 : 0 ;
247+ uint8_t thousands = (result .exception_line / 1000 ) % 10 ;
248+ thousands += thousands > 0 ? 1 : 0 ;
249+ uint8_t digit_sum = ones + tens + hundreds + thousands ;
250+ uint8_t num_places = 0 ;
251+ uint16_t line = result .exception_line ;
252+ for (int i = 0 ; i < 4 ; i ++ ) {
253+ if ((line % 10 ) > 0 ) {
254+ num_places ++ ;
255+ }
256+ line /= 10 ;
257+ }
258+ if (result .return_code == PYEXEC_EXCEPTION ) {
259+ total_exception_cycle = EXCEPTION_TYPE_LENGTH_MS * 3 + LINE_NUMBER_TOGGLE_LENGTH * digit_sum + LINE_NUMBER_TOGGLE_LENGTH * num_places ;
260+ }
261+ while (true) {
262+ #ifdef MICROPY_VM_HOOK_LOOP
263+ MICROPY_VM_HOOK_LOOP
264+ #endif
265+ if (reset_next_character ) {
266+ return true;
267+ }
268+ if (usb_rx_count > 0 ) {
269+ // Skip REPL if reset was requested.
270+ return receive_usb () == CHAR_CTRL_D ;
271+ }
272+
273+ if (!cdc_enabled_before && mp_cdc_enabled ) {
274+ if (cdc_enabled_at_start ) {
275+ mp_hal_stdout_tx_str ("\r\n\r\n" );
276+ } else {
277+ printf ("result code %d %d.\r\n" , result .return_code , result .exception_line );
278+ mp_hal_stdout_tx_str ("Auto-soft reset is on. Simply save files over USB to run them.\r\n" );
279+ }
280+ mp_hal_stdout_tx_str ("Press any key to enter the REPL and disable auto-reset. Use CTRL-D to soft reset.\r\n" );
281+ }
282+ if (cdc_enabled_before && !mp_cdc_enabled ) {
283+ cdc_enabled_at_start = false;
284+ }
285+ cdc_enabled_before = mp_cdc_enabled ;
286+
287+ uint32_t tick_diff = ticks_ms - pattern_start ;
288+ if (result .return_code != PYEXEC_EXCEPTION ) {
289+ // All is good. Ramp ALL_DONE up and down.
290+ if (tick_diff > ALL_GOOD_CYCLE_MS ) {
291+ pattern_start = ticks_ms ;
292+ tick_diff = 0 ;
293+ }
294+
295+ uint16_t brightness = tick_diff * 255 / (ALL_GOOD_CYCLE_MS / 2 );
296+ if (brightness > 255 ) {
297+ brightness = 511 - brightness ;
298+ }
299+ new_status_color (color_brightness (ALL_DONE , brightness ));
300+ } else {
301+ if (tick_diff > total_exception_cycle ) {
302+ pattern_start = ticks_ms ;
303+ tick_diff = 0 ;
304+ }
305+ // First flash the file color.
306+ if (tick_diff < EXCEPTION_TYPE_LENGTH_MS ) {
307+ if (found_main ) {
308+ new_status_color (MAIN_RUNNING );
309+ } else {
310+ new_status_color (BOOT_RUNNING );
311+ }
312+ // Next flash the exception color.
313+ } else if (tick_diff < EXCEPTION_TYPE_LENGTH_MS * 2 ) {
314+ if (mp_obj_is_subclass_fast (result .exception_type , & mp_type_IndentationError )) {
315+ new_status_color (INDENTATION_ERROR );
316+ } else if (mp_obj_is_subclass_fast (result .exception_type , & mp_type_SyntaxError )) {
317+ new_status_color (SYNTAX_ERROR );
318+ } else if (mp_obj_is_subclass_fast (result .exception_type , & mp_type_NameError )) {
319+ new_status_color (NAME_ERROR );
320+ } else if (mp_obj_is_subclass_fast (result .exception_type , & mp_type_OSError )) {
321+ new_status_color (OS_ERROR );
322+ } else {
323+ new_status_color (OTHER_ERROR );
324+ }
325+ // Finally flash the line number digits from highest to lowest.
326+ // Zeroes will not produce a flash but can be read by the absence of
327+ // a color from the sequence.
328+ } else if (tick_diff < (EXCEPTION_TYPE_LENGTH_MS * 2 + LINE_NUMBER_TOGGLE_LENGTH * digit_sum )) {
329+ uint32_t digit_diff = tick_diff - EXCEPTION_TYPE_LENGTH_MS * 2 ;
330+ if ((digit_diff % LINE_NUMBER_TOGGLE_LENGTH ) < (LINE_NUMBER_TOGGLE_LENGTH / 2 )) {
331+ new_status_color (BLACK );
332+ } else if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * thousands ) {
333+ if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH ) {
334+ new_status_color (BLACK );
335+ } else {
336+ new_status_color (THOUSANDS );
337+ }
338+ } else if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * (thousands + hundreds )) {
339+ if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * (thousands + 1 )) {
340+ new_status_color (BLACK );
341+ } else {
342+ new_status_color (HUNDREDS );
343+ }
344+ } else if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * (thousands + hundreds + tens )) {
345+ if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * (thousands + hundreds + 1 )) {
346+ new_status_color (BLACK );
347+ } else {
348+ new_status_color (TENS );
349+ }
350+ } else {
351+ if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * (thousands + hundreds + tens + 1 )) {
352+ new_status_color (BLACK );
353+ } else {
354+ new_status_color (ONES );
355+ }
356+ }
357+ } else {
358+ new_status_color (BLACK );
359+ }
360+ }
361+ }
222362}
223363
224364#ifdef UART_REPL
@@ -283,7 +423,7 @@ void samd21_init(void) {
283423 // port_pin_set_config(MICROPY_HW_LED1, &pin_conf);
284424 // port_pin_set_output_level(MICROPY_HW_LED1, false);
285425
286- neopixel_status_init ();
426+ rgb_led_status_init ();
287427}
288428
289429int main (int argc , char * * argv ) {
@@ -307,24 +447,29 @@ int main(int argc, char **argv) {
307447 udc_start ();
308448 #endif
309449
310- // Run boot and main.
311- start_mp ();
312-
313450 // Main script is finished, so now go into REPL mode.
314451 // The REPL mode can change, or it can request a soft reset.
315- int exit_code = 0 ;
452+ int exit_code = PYEXEC_FORCED_EXIT ;
453+ bool skip_repl = true;
454+ bool first_run = true;
316455 for (;;) {
317- new_status_color (0x3f , 0x3f , 0x3f );
318- if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL ) {
319- exit_code = pyexec_raw_repl ();
320- } else {
321- exit_code = pyexec_friendly_repl ();
456+ if (!skip_repl ) {
457+ autoreset_disable ();
458+ new_status_color (REPL_RUNNING );
459+ if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL ) {
460+ exit_code = pyexec_raw_repl ();
461+ } else {
462+ exit_code = pyexec_friendly_repl ();
463+ }
322464 }
323465 if (exit_code == PYEXEC_FORCED_EXIT ) {
324- mp_hal_stdout_tx_str ("soft reboot\r\n" );
325- reset_samd21 ();
326- reset_mp ();
327- start_mp ();
466+ if (!first_run ) {
467+ mp_hal_stdout_tx_str ("soft reboot\r\n" );
468+ reset_samd21 ();
469+ reset_mp ();
470+ }
471+ first_run = false;
472+ skip_repl = start_mp ();
328473 } else if (exit_code != 0 ) {
329474 break ;
330475 }
0 commit comments