5959
6060# AT commands from the Arduino library
6161# Tested
62- CMD_AT_ACTION = const ( "ACTION" )
63- CMD_AT_ACTION_STATUS = const ( "ACTION?" )
64- CMD_AT_ALGOS = const ( "ALGOS?" )
65- CMD_AT_BREAK = const ( "BREAK" )
66- CMD_AT_ID = const ( "ID?" )
67- CMD_AT_INFO = const ( "INFO?" )
68- CMD_AT_INVOKE = const ( "INVOKE" )
69- CMD_AT_MODEL = const ( "MODEL" ) # e.g. MODEL=1
70- CMD_AT_MODELS = const ( "MODELS?" )
71- CMD_AT_MODEL_STATUS = const ( "MODEL?" )
72- CMD_AT_NAME = const ( "NAME?" )
73- CMD_AT_RESET = const ( "RST" )
74- CMD_AT_SAMPLE = const ( "SAMPLE" )
75- CMD_AT_SAMPLE_STATUS = const ( "SAMPLE?" )
76- CMD_AT_SAVE_JPEG = const ( "save_jpeg()" ) # not tested with SD card
77- CMD_AT_SENSOR = const ( "SENSOR" )
78- CMD_AT_SENSORS = const ( "SENSORS?" )
79- CMD_AT_SENSOR_STATUS = const ( "SENSOR?" )
80- CMD_AT_STATUS = const ( "STAT?" )
81- CMD_AT_VERSION = const ( "VER?" )
62+ CMD_AT_ACTION = "ACTION"
63+ CMD_AT_ACTION_STATUS = "ACTION?"
64+ CMD_AT_ALGOS = "ALGOS?"
65+ CMD_AT_BREAK = "BREAK"
66+ CMD_AT_ID = "ID?"
67+ CMD_AT_INFO = "INFO?"
68+ CMD_AT_INVOKE = "INVOKE"
69+ CMD_AT_MODEL = "MODEL" # e.g. MODEL=1
70+ CMD_AT_MODELS = "MODELS?"
71+ CMD_AT_MODEL_STATUS = "MODEL?"
72+ CMD_AT_NAME = "NAME?"
73+ CMD_AT_RESET = "RST"
74+ CMD_AT_SAMPLE = "SAMPLE"
75+ CMD_AT_SAMPLE_STATUS = "SAMPLE?"
76+ CMD_AT_SAVE_JPEG = "save_jpeg()" # not tested with SD card
77+ CMD_AT_SENSOR = "SENSOR"
78+ CMD_AT_SENSORS = "SENSORS?"
79+ CMD_AT_SENSOR_STATUS = "SENSOR?"
80+ CMD_AT_STATUS = "STAT?"
81+ CMD_AT_VERSION = "VER?"
8282
8383# Recognized but don't know args yet
84- CMD_AT_LED = const ("led" )
85-
86- # 'name' values in event responses
87- EVENT_INVOKE = const ("INVOKE" )
88- EVENT_SAMPLE = const ("SAMPLE" )
89- EVENT_SUPERVISOR = const ("SUPERVISOR" )
84+ CMD_AT_LED = "led"
9085
9186# 'type' values in responses
92- CMD_TYPE_RESPONSE = const (0 )
93- CMD_TYPE_EVENT = const (1 )
94- CMD_TYPE_LOG = const (2 )
87+ _CMD_TYPE_RESPONSE = const (0 )
88+ _CMD_TYPE_EVENT = const (1 )
89+ _CMD_TYPE_LOG = const (2 )
9590
9691# 'code' values in responses
97- CMD_OK = const ( 0 )
98- CMD_AGAIN = const ( 1 )
99- CMD_ELOG = const ( 2 )
100- CMD_ETIMEDOUT = const ( 3 )
101- CMD_EIO = const ( 4 )
102- CMD_EINVAL = const ( 5 )
103- CMD_ENOMEM = const ( 6 )
104- CMD_EBUSY = const ( 7 )
105- CMD_ENOTSUP = const ( 8 )
106- CMD_EPERM = const ( 9 )
107- CMD_EUNKNOWN = const ( 10 )
92+ CMD_OK = 0
93+ CMD_AGAIN = 1
94+ CMD_ELOG = 2
95+ CMD_ETIMEDOUT = 3
96+ CMD_EIO = 4
97+ CMD_EINVAL = 5
98+ CMD_ENOMEM = 6
99+ CMD_EBUSY = 7
100+ CMD_ENOTSUP = 8
101+ CMD_EPERM = 9
102+ CMD_EUNKNOWN = 10
108103
109104RESPONSE_PREFIX = const (b"\r {" )
110105RESPONSE_SUFFIX = const (b"}\n " )
111106
107+ _CODE_DECODE_TABLE = {
108+ 0 : "OK" ,
109+ 1 : "AGAIN" ,
110+ 2 : "ELOG" ,
111+ 3 : "ETIMEDOUT" ,
112+ 4 : "EIO" ,
113+ 5 : "EINVAL" ,
114+ 6 : "ENOMEM" ,
115+ 7 : "EBUSY" ,
116+ 8 : "ENOTSUP" ,
117+ 9 : "EPERM" ,
118+ 10 : "EUNKNOWN" ,
119+ }
112120
113121class DecodeError (ValueError ):
114122 pass
@@ -489,7 +497,7 @@ def _parse_image(self, data: dict) -> None:
489497 self ._image = None
490498
491499 def _parse_event (self , response : dict ) -> None :
492- """Handle a JSON event response (type=CMD_TYPE_EVENT ).
500+ """Handle a JSON event response (type=_CMD_TYPE_EVENT ).
493501
494502 Extracts inference results (boxes, classes, points, keypoints, images)
495503 from INVOKE and SAMPLE events and updates instance attributes.
@@ -512,7 +520,7 @@ def _parse_event(self, response: dict) -> None:
512520 self ._parse_image (data )
513521
514522 def _parse_log (self , response : dict ) -> None :
515- """Handle a log JSON response (type=CMD_TYPE_LOG )."""
523+ """Handle a log JSON response (type=_CMD_TYPE_LOG )."""
516524 # print(response)
517525 pass
518526
@@ -525,9 +533,9 @@ def _wait(self, response_type: int, cmd: str, timeout: float = 1.0) -> int:
525533 response = self ._response = self ._parse_json (resp )
526534
527535 retval : int = response ["code" ]
528- if response ["type" ] == CMD_TYPE_EVENT :
536+ if response ["type" ] == _CMD_TYPE_EVENT :
529537 self ._parse_event (response )
530- elif response ["type" ] == CMD_TYPE_LOG :
538+ elif response ["type" ] == _CMD_TYPE_LOG :
531539 self ._parse_log (response )
532540 return retval
533541
@@ -574,8 +582,8 @@ def invoke(self, times: int, diffonly: bool, resultonly: bool, timeout: float =
574582 ... print(f"Detected object at ({box.x}, {box.y})")
575583 """
576584 self ._send_command (f"{ CMD_AT_INVOKE } ={ times } ,{ int (diffonly )} ,{ int (resultonly )} " )
577- if (err := self ._wait (CMD_TYPE_RESPONSE , CMD_AT_INVOKE , 0.05 )) == CMD_OK :
578- return self ._wait (CMD_TYPE_EVENT , CMD_AT_INVOKE , timeout )
585+ if (err := self ._wait (_CMD_TYPE_RESPONSE , CMD_AT_INVOKE , 0.05 )) == CMD_OK :
586+ return self ._wait (_CMD_TYPE_EVENT , CMD_AT_INVOKE , timeout )
579587 return err
580588
581589 def sample_image (self , times : int = 1 , timeout : float = 0.1 ) -> int :
@@ -596,8 +604,8 @@ def sample_image(self, times: int = 1, timeout: float = 0.1) -> int:
596604 ... f.write(ai.image.data)
597605 """
598606 self ._send_command (f"{ CMD_AT_SAMPLE } ={ times } " )
599- if (err := self ._wait (CMD_TYPE_RESPONSE , CMD_AT_SAMPLE , 0.05 )) == CMD_OK :
600- return self ._wait (CMD_TYPE_EVENT , CMD_AT_SAMPLE , timeout )
607+ if (err := self ._wait (_CMD_TYPE_RESPONSE , CMD_AT_SAMPLE , 0.05 )) == CMD_OK :
608+ return self ._wait (_CMD_TYPE_EVENT , CMD_AT_SAMPLE , timeout )
601609 return err
602610
603611 def id (self , cache : bool = True ) -> str | None :
@@ -613,7 +621,7 @@ def id(self, cache: bool = True) -> str | None:
613621 return self ._id
614622
615623 self ._send_command (CMD_AT_ID )
616- if self ._wait (CMD_TYPE_RESPONSE , CMD_AT_ID ) == CMD_OK :
624+ if self ._wait (_CMD_TYPE_RESPONSE , CMD_AT_ID ) == CMD_OK :
617625 if self ._response :
618626 self ._id = self ._response ["data" ]
619627 return self ._id
@@ -632,7 +640,7 @@ def name(self, cache: bool = True) -> str | None:
632640 return self ._name
633641
634642 self ._send_command (CMD_AT_NAME )
635- if self ._wait (CMD_TYPE_RESPONSE , CMD_AT_NAME , 3.0 ) == CMD_OK :
643+ if self ._wait (_CMD_TYPE_RESPONSE , CMD_AT_NAME , 3.0 ) == CMD_OK :
636644 if self ._response :
637645 self ._name = self ._response ["data" ]
638646 return self ._name
@@ -651,7 +659,7 @@ def version(self, cache: bool = True) -> dict | None:
651659 return self ._version
652660
653661 self ._send_command (CMD_AT_VERSION )
654- if self ._wait (CMD_TYPE_RESPONSE , CMD_AT_VERSION ) == CMD_OK :
662+ if self ._wait (_CMD_TYPE_RESPONSE , CMD_AT_VERSION ) == CMD_OK :
655663 if self .response :
656664 self ._version = self .response ["data" ]
657665 return self ._version
@@ -683,7 +691,7 @@ def info(self, cache: bool = True) -> str | None:
683691 return self ._info
684692
685693 self ._send_command (CMD_AT_INFO )
686- if self ._wait (CMD_TYPE_RESPONSE , CMD_AT_INFO , 3.0 ) == CMD_OK :
694+ if self ._wait (_CMD_TYPE_RESPONSE , CMD_AT_INFO , 3.0 ) == CMD_OK :
687695 if self ._response :
688696 self ._info = self ._response ["data" ]["info" ]
689697 return self ._info
@@ -733,7 +741,7 @@ def clean_actions(self) -> int:
733741 CMD_OK (0) on success, or error code.
734742 """
735743 self ._send_command (f'{ CMD_AT_ACTION } =""' )
736- return self ._wait (CMD_TYPE_RESPONSE , CMD_AT_ACTION )
744+ return self ._wait (_CMD_TYPE_RESPONSE , CMD_AT_ACTION )
737745
738746 def save_jpeg (self ) -> int :
739747 """Configure the board to save captured images to SD card.
@@ -745,7 +753,7 @@ def save_jpeg(self) -> int:
745753 CMD_OK (0) on success, or error code.
746754 """
747755 self ._send_command (f'{ CMD_AT_ACTION } ="{ CMD_AT_SAVE_JPEG } "' )
748- return self ._wait (CMD_TYPE_RESPONSE , CMD_AT_ACTION )
756+ return self ._wait (_CMD_TYPE_RESPONSE , CMD_AT_ACTION )
749757
750758 def perform_command (self , cmd : str , tag : str | None = None ) -> int :
751759 """Perform a raw AT command.
@@ -767,13 +775,25 @@ def perform_command(self, cmd: str, tag: str | None = None) -> int:
767775 while retries < 2 :
768776 self ._send_command (cmd , tag )
769777 try :
770- return self ._wait (CMD_TYPE_RESPONSE , cmd )
778+ return self ._wait (_CMD_TYPE_RESPONSE , cmd )
771779 # Retry on decode error
772780 except DecodeError :
773781 retries += 1
774782 continue
775783 return CMD_ETIMEDOUT
776784
785+ @staticmethod
786+ def get_error_name (code : int ) -> str :
787+ """Get the string name for a given error code.
788+
789+ Args:
790+ code: The integer error code.
791+
792+ Returns:
793+ The string representation of the error code (e.g., "OK", "ETIMEDOUT").
794+ """
795+ return _CODE_DECODE_TABLE .get (code , f"UNKNOWN({ code } )" )
796+
777797
778798__name__ = "grove_vision_ai_v2"
779799__version__ = "0.1.0"
0 commit comments