{"id":15177783,"url":"https://github.com/ams-osram/osp_aocmd","last_synced_at":"2026-03-07T03:02:28.649Z","repository":{"id":252424270,"uuid":"840400482","full_name":"ams-OSRAM/OSP_aocmd","owner":"ams-OSRAM","description":"A library with a command interpreter (over UART/USB) and handlers for OSP telegrams.","archived":false,"fork":false,"pushed_at":"2025-09-17T11:56:27.000Z","size":425,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-09-17T13:34:20.476Z","etag":null,"topics":["arduino","as1163","e3731i","library","osp","rgb-led"],"latest_commit_sha":null,"homepage":"https://github.com/ams-OSRAM/OSP_aotop","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ams-OSRAM.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"license.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-08-09T16:08:54.000Z","updated_at":"2025-09-17T11:55:42.000Z","dependencies_parsed_at":"2024-08-09T18:02:37.370Z","dependency_job_id":"56484522-9503-4941-86be-dc004b3ea15b","html_url":"https://github.com/ams-OSRAM/OSP_aocmd","commit_stats":{"total_commits":9,"total_committers":2,"mean_commits":4.5,"dds":"0.33333333333333337","last_synced_commit":"6e4d0c26717f603ef98383840c57525b232cf5ab"},"previous_names":["ams-osram-group/osp_aocmd","ams-osram/osp_aocmd"],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/ams-OSRAM/OSP_aocmd","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ams-OSRAM%2FOSP_aocmd","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ams-OSRAM%2FOSP_aocmd/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ams-OSRAM%2FOSP_aocmd/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ams-OSRAM%2FOSP_aocmd/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ams-OSRAM","download_url":"https://codeload.github.com/ams-OSRAM/OSP_aocmd/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ams-OSRAM%2FOSP_aocmd/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30206339,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-06T19:07:06.838Z","status":"online","status_checked_at":"2026-03-07T02:00:06.765Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arduino","as1163","e3731i","library","osp","rgb-led"],"created_at":"2024-09-27T14:43:15.642Z","updated_at":"2026-03-07T03:02:28.632Z","avatar_url":"https://github.com/ams-OSRAM.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# OSP CommandInterpreter aocmd\n\nLibrary \"OSP CommandInterpreter aocmd\", usually abbreviated to \"aocmd\", \nis one of the **aolibs**; short for Arduino OSP libraries from ams-OSRAM.\nThis suite implements support for chips that use the Open System Protocol, \nlike the AS1163 (\"SAID\") or the OSIRE E3731i (\"RGBi\").\nThe landing page for the _aolibs_ is on \n[GitHub](https://github.com/ams-OSRAM/OSP_aotop).\n\n\n## Introduction\n\nLibrary _aocmd_ contains a command interpreter and several commands.\nThe interesting command is \"osp\"; it allows sending and receiving OSP telegrams.\n\n![aocmd in context](extras/aolibs-aocmd.drawio.png)\n\nThe command interpreter can be extended with application specific commands.\nFurthermore, a command file (`boot.cmd`)can be stored persistently on the \nESP32, and executed at startup. \n\n\n### System setup\n\nThe diagram below shows the system architecture from a command interpreter point of view.\n\n![Sytem architecture](extras/system.drawio.png)\n\nOn the right hand side, we see the OSP32 board with some SAIDs.\nThey are controlled by the firmware in the ESP, more specifically, \nby the OSP library (aoosp) which builds on top of the SPI library (aospi).\n\nOn the left hand side, we see a PC. It runs an application known as \n_terminal_. A famous one is [putty](https://www.putty.org/), but the\nSerial Monitor in the Arduino IDE is also fine. Terminals allow a user\nto enter commands (characters) and send them over a serial link to a \n\"remote\" (in our case OSP32). When the remote responds (with characters), \ndata is sent back over the serial line and displayed by the terminal.\n\nSince PC's no longer have serial ports, a serial-to-USB chip is used.\nThe ESP32S3 DevKit board employs an \"CP210x USB to UART Bridge\"\nand the PC needs to have a driver for such a virtual COM port. \nTypically, the Arduino IDE installs those drivers. If not, download and \ninstall the [driver](https://www.silabs.com/developers/usb-to-uart-bridge-vcp-drivers).\n\nCommands that the PC sends, arrive at the command interpreter, a part of the\nESP firmware. Commands like \"echo\", \"help\", \"file\", \"osp\" are received\nand dispatched to command _handlers_. Those parse and then execute them,\nfor example by calling the _aoosp_ library.\n\nThere is one more feature: the ESP has a persistent memory (EEPROM).\nThe command interpreter has a command to show, enter an execute one \ncommand file (known as `boot.cmd`). This command file is automatically\nexecuted on power-on-reset. The maximum size of `boot.cmd` is 2k byte.\n\n\n### Example commands\n\nThis section gives a flavor of the stock commands. It is possible to \nadd your own, and for example the _aomw_ and _aoapps_ libraries add \n(more high-level) commands. This does mean that it depends on the firmware \nflashed to the ESP32 which commands are available. To follow the below \ncommand fragments it is suggested to use the firmware\n[osplink](https://github.com/ams-OSRAM/OSP_aotop/tree/main/examples/osplink).\nOthers, like [saidbasic](https://github.com/ams-OSRAM/OSP_aotop/tree/main/examples/saidbasic)\nalso include all commands (and some more), but the included apps need to be \nstopped first otherwise their control of the OSP chain might interfere with \nthe entered commands.\n\n\n#### General commands\n\nOnce a terminal is connected and the OSP32 board is (re)booted, we are greeted \nwith a banner.\n\n```\n  ____   _____ _____    _ _       _\n / __ \\ / ____|  __ \\  | (_)     | |\n| |  | | (___ | |__) | | |_ _ __ | | __\n| |  | |\\___ \\|  ___/  | | | '_ \\| |/ /\n| |__| |____) | |      | | | | | |   \u003c\n \\____/|_____/|_|      |_|_|_| |_|_|\\_\\\nOSPlink - version 1.7\n\nspi: init\nosp: init\ncmd: init\nmw: init\nui32: init\n\nNo 'boot.cmd' file available to execute\nType 'help' for help\n\u003e\u003e \n```\n\nA prompt `\u003e\u003e` is printed. This means that commands can be entered.\nThe easiest commands are `version` and `echo` (shown below with an extra CR \nin between):\n\n```\n\u003e\u003e version\napp     : OSPlink 1.7\nruntime : Arduino ESP32 3_0_3\ncompiler: 12.2.0\narduino : 10607 (likely IDE2.x)\ncompiled: Sep  3 2024, 09:41:17\naolibs  : result 0.4.0 spi 0.5.0 osp 0.4.0 cmd 0.5.1\n\u003e\u003e \n\u003e\u003e echo Hello, world!\nHello, world!\n\u003e\u003e\n```\n\nA better place to start however, is the `help` command. \n\n```\n\u003e\u003e help\nAvailable commands\nboard - board info and commands\necho - echo a message (or en/disables echoing)\nfile - manages the file 'boot.cmd' with commands run at startup\nhelp - gives help (try 'help help')\nosp - sends and receives OSP telegrams\nsaid - sends and receives SAID specific telegrams\ntopo - build, query and use topology\nversion - version of this application, its libraries and tools to build it\n```\n\nAs we see, the `help` command in isolation lists all commands.\nIt can also give help on a specific command; just append the command name.\nHelp on `version` is nicely short:\n\n```\n\u003e\u003e help version\nSYNTAX: version\n- lists version of this application, its libraries and tools to build it\nNOTES:\n- supports @-prefix to suppress output\n```\n\nThe help for the `echo` command is longer; but the `help` command allows \nselecting one \"topic\". Below the topic (sub command) `wait` of `echo` is \nexplained.\n\n```\n\u003e\u003e help echo wait\nSYNTAX: echo wait \u003ctime\u003e\n- waits \u003ctime\u003e ms (might be useful in scripts)\n```\n\nThe command interpreter allows commands to be shortened (even to \none character). So `version`, `ver` and `v` all give the same result. \nShortening is also supported for sub commands, so \n`help version`, `help ver` and `help v` \nall give the same result. The two flavors of shortening can be combined:\n\n```\n\u003e\u003e h v\nSYNTAX: version\n- lists version of this application, its libraries and tools to build it\nNOTES:\n- supports @-prefix to suppress output\n```\n\nSome commands support a `@` as prefix; it suppresses output of that command.\nFor example, setting the direction mux, gives feedback on the final state. \nThis can be suppressed by prepending a `@`. The fragment below illustrates \nthat, and it also shows that comments start with `//`.\n\n\n```\n\u003e\u003e // comment without command\n\u003e\u003e osp dirmux loop // shows new status\ndirmux: loop\n\u003e\u003e @osp dirmux loop // with @ no feedback on new status\n\u003e\u003e\n```\n\nIn the above command the `@` suppresses _all_ output, in other commands \nthe `@` only _reduces_ the output. For example, `@help` on a sub command reduces \nto only the section headers.\n\n```\n\u003e\u003e @help echo\nSYNTAX: echo [line] \u003cword\u003e...\nSYNTAX: echo faults [step]\nSYNTAX: echo [ enabled | disabled ]\nSYNTAX: echo wait \u003ctime\u003e\nNOTES:\n```\n\n\n#### Boot.cmd\n\nThe command interpreter has a small local file system that can store a \nsingle file `boot.cmd`. To create `boot.cmd` use the `file record` command, \nand enter line after line, terminating with an empty line.\n\n```\n\u003e\u003e file record\n001\u003e\u003e echo Hello, world!\n002\u003e\u003e @version // short version\n003\u003e\u003e \nfile: 28 bytes written\n```\n\nNote that while the `boot.cmd` file is recorded, the prompt shows line \nnumbers (eg `001\u003e\u003e`).\n\nThe file `boot.cmd` can be inspected,\n\n```\n\u003e\u003e file show\nfile: 'boot.cmd' content:\necho Hello, world!\n@version\n```\n\nand executed.\n\n```\n\u003e\u003e file exec\n\u003e\u003e echo Hello, world!\nHello, world!\n\u003e\u003e @version\nSAIDbasic 2.4\n\u003e\u003e \n```\n\nAs the name suggests the file `boot.cmd` will be executed when the ESP32 \n(cold) boots (officially known as POR - power-on-reset). When we press \nthe reset (RST) button on OSP32, we see the script in action.\n\n```\n  ____   _____ _____    _ _       _\n / __ \\ / ____|  __ \\  | (_)     | |\n| |  | | (___ | |__) | | |_ _ __ | | __\n| |  | |\\___ \\|  ___/  | | | '_ \\| |/ /\n| |__| |____) | |      | | | | | |   \u003c\n \\____/|_____/|_|      |_|_|_| |_|_|\\_\\\nOSPlink - version 1.7\n\nspi: init\nosp: init\ncmd: init\nmw: init\nui32: init\n\nRunning 'boot.cmd'\n\u003e\u003e echo Hello, world!\nHello, world!\n\u003e\u003e @version\nOSPlink 1.7\n\u003e\u003e \n\nType 'help' for help\n\u003e\u003e \n```\n\nIn practice, `boot.cmd` is used to configure a demo. For example \n[saidbasic](https://github.com/ams-OSRAM/OSP_aotop/tree/main/examples/saidbasic)\ncould have the following lines in `boot.cmd` to configure the overall \nbrightness and to define the available flags. Note the `@` symbols; they \nsuppress output, which makes sense in a command file.\n\n\n```\n@topo dim 50\n@apps conf swflag set  europe dutch italy mali\n```\n\nTo erase the `boot.cmd`, give the `file record` command and immediately \nterminate recording by entering an empty line (pressing CR).\n\n```\n\u003e\u003e file record\n001\u003e\u003e \nfile: 0 bytes written\n\u003e\u003e file show\nfile: 'boot.cmd' empty\n\u003e\u003e \n```\n\nWhere the standard command `version` gives information about _software_, the\nstandard command `board` gives information on the ESP _hardware_.\n\n```\n\u003e\u003e board\nchip : model ESP32-S3 (2 cores) rev 0\nclk  : 240 MHz (xtal 40 MHz)\nftrs : 2.4GHz-WiFi Bluetooth-LE\nflash: 4194304 byte external flash\napp  : 378544 byte\nreset: power-on\n```\n\nAn interesting sub command of `board`, especially during development, is `reboot`;\nit resets the ESP. This is a software reset, not a power-on-reset, it\ndoes _not_ execute `boot.cmd`. In other words `reboot` invokes a clean \nrestart of the ESP. Note however, that the OSP nodes and the OLED remain \npowered, so they keep their state, unless the ESP firmware resets them. \n\nIf `boot.cmd` is needed after `board reboot`, give command `file exec`.\n\n\n#### OSP generic commands\n\nThe commands presented until now (`echo`, `help`, `version`, `board`, `file`)\nare managerial. The commands `osp` and `said` allow manipulating and \ninspecting OSP nodes.\n\n\u003e The below fragments assume that a SAIDbasic board is connected to the \n\u003e OSP32 board in loop mode (cable from OSP32 OUT to SAIDbasic IN and a cable\n\u003e from SAIDbasic OUT to OSP32 IN).\n\u003e\n\u003e The fragments also assume that no firmware is running that sends telegrams. \n\u003e The easiest way is to use a firmware that is \"command interpreter only\" like\n\u003e [osplink](https://github.com/ams-OSRAM/OSP_aotop/tree/main/examples/osplink).\n\u003e If you use a firmware that has _apps_ like \n\u003e [saidbasic](https://github.com/ams-OSRAM/OSP_aotop/tree/main/examples/saidbasic)\n\u003e the apps continuously send telegrams and that has to be stopped first.\n\u003e This can be done by activating the do-nothing app via the command `apps switch voidapp`.\n\u003e\n\u003e Some fragments assume `reset` and `init` telegrams (after setting the dirmux) \n\u003e have been sent (the `osp restinit` does all three).\n\nThe `osp` command is able to enumerate which nodes are in the OSP chain\n(and with that estimate the maximum power usage).\n\n```\n\u003e\u003e osp enum\n mcu N001 00000040/SAID T0 T1 I0 lvds\nlvds N002 00000040/SAID T2 T3 T4 lvds\nlvds N003 00000040/SAID T5 T6 T7 lvds\nlvds N004 00000000/RGBI T8 lvds\nlvds N005 00000040/SAID T9 T10 I1 lvds\nlvds N006 00000000/RGBI T11 lvds\nlvds N007 00000000/RGBI T12 lvds\nlvds N008 00000000/RGBI T13 lvds\nlvds N009 00000040/SAID T14 T15 T16 eol\nnodes(N) 1..9, triplets(T) 0..16, i2cbridges(I) 0..1, dir loop\ncount rgbi 4 said 5\nmaxpower 12x50mA + 15x48mA + 15x24mA + 9x24mA = 1.896A (9.480W)\n\u003e\u003e \n```\n\nThis shows there are 9 nodes, the first (`mcu`) and last (`eol`) both connected \nto the ESP (`dir loop`). Nodes 001, 002, 003, 005 and 009 are SAIDs, the\nother 4 RGBIs. Two SAIDs (001 and 005) have an I2C bridge enabled (I0 and I1).\nIn total there are 17 RGB triplets (4 RGBIs, 3 SAIDs with 3, 2 SAIDs with 2).\n\nThe `osp` command has information on all (currently known) telegrams (\"user manual\").\nThis information is retrieved with the `info` sub command. In isolation,\nthat command lists all telegram types. With a telegram name appended\nit lists all details of that telegram type. To know more about telegram type \n`readpwmchn` enter\n\n```\n\u003e\u003e osp info readpwmchn\nTELEGRAM 4E: readpwmchn\nDESCRIPTION: Returns current PWM setting (for all three LEDs) of the \n             requested channel.\nCASTING    : uni \nPAYLOAD    : 1 (chn); response 6 (red1 red0 grn1 grn0 blu1 blu0)\nSTATUS REQ : no (no sr possible)\nDUPLICATE  : 4E/readpwm\n```\n\nThis information explains (1st line) that `readpwmchn` has telegram id 0x4E, \nand that it is sent using unicast (3rd line) not broadcast or serial cast \n(those are never supported for telegrams that return some information).\nThe description (2nd line) explains that the telegram returns RGB settings \nfor one channel. The 4th line shows the telegram payload bytes \nin detail: the command telegram has 1 byte, the channel, the response \ntelegram has 6 bytes, two for each color. No status (line 5) will be \nreturned (several commands have as feature that the node acknowledges \nwith a status response); as this is not possible for telegrams that \nreturn some information. Finally this identifies that there is a \ntelegram with the same ID but different behavior (in this case that is\nthe very similar `readpwm` without a channel).\n\n#### High level OSP\n\nThere is a \"high level\" way to send OSP telegrams.\nThe firmware will fill in the preamble, payload size indicator, telegram id and crc.\nThe following high level command sequence switches on two RGBs;\nthe first (SAID 001 channel 0) to green, the second (SAID 2 channel 0)\nto blue. The info for `setpwmchn` explains the 8 bytes payload: \n`chn unused red1 red0 grn1 grn0 blu1 blu0` (the reason for the unused byte FF\nis that OSP telegrams can have a length of 6 or 8, but not 7).\n\n```\nosp send 000 reset\nosp send 001 initbidir\nosp send 000 clrerror\nosp send 000 goactive\nosp send 001 setpwmchn 00  FF  00 00  11 11  00 00\nosp send 002 setpwmchn 00  FF  00 00  00 00  11 11\n```\n\nThis is the sequence in action.\n\n```\n\u003e\u003e osp dirmux bidir // ensure the mux matches the init (below)\ndirmux: bidir\n\u003e\u003e\n\u003e\u003e osp send 000 reset \ntx A0 00 00 22\nrx none ok\n\u003e\u003e osp send 001 initbidir // matches dirmux\ntx A0 04 02 A9\nrx A0 25 02 6B 50 7F (230 us) ok\n\u003e\u003e osp send 000 clrerror \ntx A0 00 01 0D\nrx none ok\n\u003e\u003e osp send 000 goactive \ntx A0 00 05 B1\nrx none ok\n\u003e\u003e osp send 001 setpwmchn 00 FF 00 00 11 11 00 00 \ntx A0 07 CF 00 FF 00 00 11 11 00 00 49\nrx none ok\n\u003e\u003e osp send 002 setpwmchn 00 FF 00 00 00 00 11 11\ntx A0 0B CF 00 FF 00 00 00 00 11 11 5D\nrx none ok\n```\n\nA typical initialization sequence is as follows. \nThe broadcast (000) of the `reset` telegram resets all nodes;\nthis also clears there addresses. The serial cast of `initloop` reassigns\naddresses (typically starting at 001). Make sure the direction mux is configured\nto match the wiring of the demo board, and make sure the `initloop`/`initbidir`\nmatches the direction mux.\n\n```\nosp send 000 reset\nosp dirmux loop\nosp send 001 initloop\n```\n\nor \n\n```\nosp send 000 reset\nosp dirmux bidir\nosp send 001 initbidir\n```\n\nThese two sequences can be abbreviated to\n\n```\nosp resetinit\n```\n\nwhich first tries Loop, and then BiDir (and also controls the dirmux).\n\n\n#### Low level OSP\n\nThe above \"high level\" commands do show the raw bytes being transferred.\nThere is also a \"low level\" way to send OSP telegrams. This is especially \nuseful for testing. For low level telegrams, the user has \nto enter all details (all bytes); but there is a feature to get the CRC computed. \nWe can use the high level commands first to get those details.\n\n```\nosp tx A0 00 00 22 // 000 reset\nosp tx A0 04 02 A9 // 001 initbidir\nosp tx A0 00 01 0D // 000 clrerror\nosp tx A0 00 05 B1 // 000 goactive\nosp tx A0 07 CF 00 FF 00 00 11 11 00 00 49 // 001 setpwmchn 00 grn\nosp tx A0 0B CF 00 FF 00 00 00 00 11 11 5D // 002 setpwmchn 00 blu\n```\n\nIf commands are sent, they are validated (see `osp validate`).\nEven if validation fails, for example because the CRC doesn't match, \nor the payload doesn't match the size indicator, the telegram is still sent. \nThis allows for testing error behavior also.\n\nValidation can be switched off (to make transfers faster) with `osp validate disable`.\nThere are other managerial subcommands (`osp log` and `osp count` and to\nsome extend `osp hwtest`).\n\nHere is an example with validation triggered; we send goactive with a \npayload byte FF (where it has none).\n\n```\n\u003e\u003e osp tx  A0 00 05 FF B1\nvalidate: 05/goactive does not have 1 bytes as payload, but 0..0\nvalidate: payload is 1 bytes so psi should be 1 but is 0 \nvalidate: crc B1 is incorrect (should be 53)\ntx A0 00 05 FF B1\nrx none ok\n```\n\nIf a node executes an erroneous telegram is implementation dependent \n(e.g. CRC checking can be disabled in SAID). But for command arguments the OSP \nspecification says \"If the length of the argument does not match the \nexpectation, an error flag is raised. The command will not be executed\".\n\nWe saw in the previous section that the response for bidir was \n`A0 25 02 6B 50 7F`. We can ask help to dissect this.\n\n```\n\u003e\u003e osp fields A0 25 02 6B 50 7F\n+---------------+---------------+---------------+---------------+---------------+---------------+\n|      A0       |      25       |      02       |      6B       |      50       |      7F       |\n|1 0 1 0 0 0 0 0|0 0 1 0 0 1 0 1|0 0 0 0 0 0 1 0|0 1 1 0 1 0 1 1|0 1 0 1 0 0 0 0|0 1 1 1 1 1 1 1|\n+-------+-------+-----------+---+-+-------------+---------------+---------------+---------------+\n|preambl|      address      | psi |   command   |    payload    |    payload    |      crc      |\n+-------+-------------------+-----+-------------+---------------+---------------+---------------+\n|  0xA  |       0x009       | 0x2 |    0x02     |     0x6B      |     0x50      |   0x7F (ok)   |\n|   -   |    unicast(9)     |  2  |  initbidir  |      107      |       80      |    127 (ok)   |\n+-------+-------------------+-----+-------------+---------------+---------------+---------------+\n```\n\n\n#### Topo for OSP\n\nSome firmware variants contain the command `topo` which supports a\nhigh abstraction in operating an OSP chain. It builds a data structure called \nthe _topology map_, which identifies how many RGB triplets there are.  For \neach triplet it records if it is a stand-alone RGBI or an RGB module connected \nto a SAID. The command `topo pwm` allows setting the color of a triplet,\nabstracting away if it is an RGBI or an RGB connected to a SAID.\n\nThe below fragment switches RGB triplet 6 to red.\nIt also sets the \"global brightness\" of the topo module \nto 50‰ (actually \"pro [kibi](https://en.wikipedia.org/wiki/Binary_prefix#kibi)\").\n\n```\n\u003e\u003e topo build // includes resetinit\nnodes(N) 1..9, triplets(T) 0..16, i2cbridges(I) 0..1, dir loop\n\u003e\u003e topo dim 50\ndim 50/1024 (said 41x, rgbi 104x below max power)\n\u003e\u003e topo pwm 6 1111 0000 0000\npwm T6: 1111 0000 0000\n```\n\n#### SAID I2C \n\nIn addition to the generic `osp` command, there is the `said` command,\nwith support specifically for the SAID chip.\n\nFor example, the fragment below scans the entire OSP chain for SAIDs, \nchecks if they have an I2C bus, and if so, scans the I2C bus.\n\n```\n\u003e\u003e said i2c 000 scan\nSAID 001 has I2C (now powered)\n  00:  00  01  02  03  04  05  06  07  08  09  0a  0b  0c  0d  0e  0f \n  10:  10  11  12  13  14  15  16  17  18  19  1a  1b  1c  1d  1e  1f \n  20:  20  21  22  23  24  25  26  27  28  29  2a  2b  2c  2d  2e  2f \n  30:  30  31  32  33  34  35  36  37  38  39  3a  3b  3c  3d  3e  3f \n  40:  40  41  42  43  44  45  46  47  48  49  4a  4b  4c  4d  4e  4f \n  50:  50  51  52  53 [54] 55  56  57  58  59  5a  5b  5c  5d  5e  5f \n  60:  60  61  62  63  64  65  66  67  68  69  6a  6b  6c  6d  6e  6f \n  70:  70  71  72  73  74  75  76  77  78  79  7a  7b  7c  7d  7e  7f \nSAID 001 has 1 I2C devices\n\nSAID 005 has I2C (now powered)\n  00:  00  01  02  03  04  05  06  07  08  09  0a  0b  0c  0d  0e  0f \n  10:  10  11  12  13  14  15  16  17  18  19  1a  1b  1c  1d  1e  1f \n  20: [20] 21  22  23  24  25  26  27  28  29  2a  2b  2c  2d  2e  2f \n  30:  30  31  32  33  34  35  36  37  38  39  3a  3b  3c  3d  3e  3f \n  40:  40  41  42  43  44  45  46  47  48  49  4a  4b  4c  4d  4e  4f \n  50: [50] 51  52  53  54  55  56  57  58  59  5a  5b  5c  5d  5e  5f \n  60:  60  61  62  63  64  65  66  67  68  69  6a  6b  6c  6d  6e  6f \n  70:  70  71  72  73  74  75  76  77  78  79  7a  7b  7c  7d  7e  7f \nSAID 005 has 2 I2C devices\n\ntotal 2 SAIDs have 3 I2C devices\n\u003e\u003e \n```\n\nThe `@` version is much less verbose\n\n```\n\u003e\u003e @said i2c 000 scan\n[54] SAID 001 has 1 I2C devices\n[20][50] SAID 005 has 2 I2C devices\ntotal 2 SAIDs have 3 I2C devices\n```\n\nBoth show that at address 001 there is a SAID with an I2C device with \naddress 54. That happens to be an (AT24C02C) EEPROM. Let's try to read\n8 bytes from address 80.\n\n```\n\u003e\u003e said i2c 001 read 54 80 8\nsaid(001).i2c.dev(54).reg(80) FF FF FF FF FF FF FF FF\n```\n\nAll bytes are FF. We change the middle four to \"DEADBEEF\". \n\n```\n\u003e\u003e said i2c 001 write 54 82 DE AD BE EF\nsaid(001).i2c.dev(54).reg(82) DE AD BE EF\n\u003e\u003e said i2c 001 read 54 80 8\nsaid(001).i2c.dev(54).reg(80) FF FF DE AD BE EF FF FF\n```\n\nAnd we restore that (using `@` for reduced output)\n\n```\n\u003e\u003e @said i2c 001 write 54 82 FF FF FF FF\n\u003e\u003e @said i2c 001 read 54 80 8\nFF FF FF FF FF FF FF FF\n```\n\n#### SAID OTP \n\nAnother (advanced) feature of the `said` command is reading the OTP.\n\n```\n\u003e\u003e said otp 001\notp: 0x0D: 09 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n          7             6             5             4             3             2             1             0\n   +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+\n0D |            CH_CLUSTERING[2:0]           |HAPTIC_DRIVER|  SPI_MODE   | SYNC_PIN_EN | STAR_NET_EN |I2C_BRIDGE_EN|\n   |                    0                    |      0      |      1      |      0      |      0      |      1      |\n   +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+\n0E |             |             |             |             | OTP_ADDR_EN |         STAR_NET_OTP_ADDR[2:0]          |\n   |      0      |      0      |      0      |      0      |      0      |                  0b000                  |\n   +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+\n1E |  CUST_LOCK  |BRANCH_POINT*|             |             |             |  SKIPCHN2*  |  SKIPCHN1*  |  SKIPCHN0*  |\n   |      0      |      0      |      0      |      0      |      0      |      0      |      0      |      0      |\n   +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+\n1F |                                                   CRC2[7:0]                                                   |\n   |                                                      0x00                                                     |\n   +-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+\n```\n\nIt is also possible to read a specific byte of the OTP. Writing a byte \nrequires a password, which can be set with the command `said password`, that \nis, if you know the password (get it from your ams OSRAM contact).\n\n```\n\u003e\u003e said otp 001 0D\nSAID[001].OTP[0D] -\u003e 09 (ok)\n\u003e\u003e said otp 001 0D 08\nWARNING: ask ams-OSRAM for TESTPW and see aoosp_said_testpw_get() for how to set it\nSAID[001].OTP[0D] \u003c- 08 (ok)\n\u003e\u003e said otp 001 0D\nSAID[001].OTP[0D] -\u003e 09 (ok)\n```\n\n\n## Examples\n\nThis library comes with the following examples.\nYou can find them in the Arduino IDE via \n[File \u003e Examples \u003e OSP CommandInterpreter aocmd \u003e ...](examples):\n\n- **aocmd_min** ([source](examples/aocmd_min))  \n  This demo initializes the command interpreter, then starts\n  polling `Serial` for incoming characters. These are buffered until an \n  end-of-line is received (either CR or LF), then the command is parsed \n  and executed. This demo does not add its own command.\n\n- **aocmd_owncmd** ([source](examples/aocmd_owncmd))  \n  This demo implements an application specific command, and\n  registers it with the command interpreter.\n  The new command has name `stat`. It allows passing several (hex) numbers.\n  The `stat` command keeps track of the count and the sum.\n\n- **aocmd_boot** ([source](examples/aocmd_boot))  \n  This demo implements an application that prints a message every x ms.\n  It adds a command (\"wait\") to set and get x.\n  This command can then be stored in `boot.cmd`, to configure this app persistently.\n\n- **aocmd_template** ([source](examples/aocmd_template))  \n  This sketch is a template for an application with a command handler.\n  It includes an application banner, it implements the upcalls from \n  the \"version\" command, and it runs `boot.cmd` on startup.\n \nThere is also an official executable - as opposed to an example - in \nanother library, namely _aotop_:\n\n- **osplink** ([source](https://github.com/ams-OSRAM/OSP_aotop/tree/main/examples/osplink))  \n  This application allows the PC (with a terminal like the Arduino Serial Monitor)\n  to send and receive OSP telegrams, using serial-over-USB. \n  \n  This library _aocmd_ comes with an _experimental_ Python \n  library [libosplink](python/libosplink) and a Python application \n  [exosplink](python/exosplink) to drive _osplink_.\n  \n\n## Module architecture\n\nThis library contains several modules, see the figure below for an overview (arrows indicate `#include`).\n\n![Modules](extras/aocmd-modules.drawio.png)\n\n- **aocmd_cint** (`aocmd_cint.cpp` and `aocmd_cint.h`) is the core module \n  of the library; it implements the command interpreter. \n  All other modules implement command handlers.\n  The command handlers are registered with the command interpreter.\n  When the user enters a command whose name matches one of the registered ones,\n  the command interpreter calls the handler.\n  \n  The command interpreter is a recycled library \n  [https://github.com/maarten-pennings/cmd](https://github.com/maarten-pennings/cmd).\n  Version 8.1.0 is used. \n  The prefix is changed from `cmd` to `aocmd_cint`, and two friend commands (`echo` and `help`) \n  were split off (put in separate files).\n  No API documentation has been added for this library.\n\n- **aocmd_echo** (`aocmd_echo.cpp` and `aocmd_echo.h`) is the first friend command of\n  the command interpreter. Friend, because it _configures_ the command interpreter,\n  e.g. enable/disable command echoing.\n\n- **aocmd_help** (`aocmd_help.cpp` and `aocmd_help.h`) is the second friend command of\n  the command interpreter. Friend, because it knows which commands are registered \n  with the command interpreter, so it can provide help on them.\n\n  Use `help xxx` to get (syntax) help on command `xxx`.\n\n- **aocmd_board** (`aocmd_board.cpp` and `aocmd_board.h`) is a generic command giving some\n  information about the board: which micro controller (revision, features), memory sizes.\n  It also allows the user to issue a software reset (`board reboot`); this resets the ESP \n  and restarts the software.\n\n  Note that `aocmd_board` has intimate knowledge of the ESP32S3, so it is \n  unlikely to run unmodified on other CPUs.\n  \n- **aocmd_version** (`aocmd_version.cpp` and `aocmd_version.h`) is a generic command listing\n  various versions: of the application, libraries, compiler, and IDE. It has two callbacks,\n  one for the application to print is version number, and one generic, for any other\n  version info an application might want to print.\n\n- **aocmd_file** (`aocmd_file.cpp` and `aocmd_file.h`) is a generic command that manages\n  one file (`boot.cmd`) stored in a simple persistent file system (EEPROM of ESP, max 2k byte).\n  The crux of `boot.cmd` is that it runs on power-on, so it can be used to configure an\n  application.\n\n- **aocmd_osp** (`aocmd_osp.cpp` with `aocmd_osp.i`, and `aocmd_osp.h`) is the core command to support the OSP\n  protocol. The `info` sub command is a \"help\" command giving all (details of all) \n  telegrams, `enum` enumerates the nodes in the OSP chain connected to the MCU, and `resetinit`\n  resets and initializes the OSP chain.\n  \n  The key sub commands are `send` and `tx`. They allows sending (and receiving the response)\n  telegrams. The `send` command is high level; the preamble, payload size, telegram id, crc\n  are supplied automatically. The `tx` is low level, all bytes have to be hand composed.\n  Both commands allow sending miss-formed telegrams (`tx` even more so than `send`). This\n  allows checking error handling behavior and border cases.\n  \n  The file `aocmd_osp.i` contains a table of all know telegrams. It is used to select\n  telegrams by name, provide info on telegrams, and to validate the fields of telegrams, \n  like the payload size.\n  \n- **aocmd_said** (`aocmd_said.cpp` and `aocmd_said.h`) command is for specific \n  SAID features. For example it supports sub commands to read and write \n  OTP memory and I2C messages.\n\n\n## API\n\nThe header [aocmd.h](src/aocmd.h) contains the API of this library.\nIt includes the module headers \n[aocmd_cint.h](src/aocmd_cint.h), \n[aocmd_echo.h](src/aocmd_echo.h), \n[aocmd_help.h](src/aocmd_help.h), \n[aocmd_board.h](src/aocmd_board.h), \n[aocmd_version.h](src/aocmd_version.h), \n[aocmd_file.h](src/aocmd_file.h), \n[aocmd_osp.h](src/aocmd_osp.h), and\n[aocmd_said.h](src/aocmd_said.h).\n\nThe headers (h files) contain little documentation; for details see the \nmodule sources (cpp files). \n\n\n### aocmd\n\n- `aocmd_init()` initializes the library (command interpreter, file system, osp info tables).\n- `AOCMD_VERSION`  identifies the version of the library.\n- `aocmd_register()` registers all commands from this library - a shorthand \n  for calling the register of all commands contained in the library individually.\n\n\n### aocmd_cint\n\nThis module is recycled from the public domain. The header file contains \none-line explanations of each function. Here we will just give a high level \noverview.\n\n- There are several macros `AOCMD_CINT_XXX` which configure the \"size\" of the \n  command interpreter, like the maximum number of registered commands or the \n  maximum length of a command line.\n\n- For applications _using_ a command line the key functions (after `aocmd_init()`) \n  are `aocmd_cint_prompt()` and `aocmd_cint_pollserial()`.\n  \n  ```c++\n  void setup() {\n    Serial.begin(115200);\n    ...\n    aocmd_init(); \n    aocmd_register();\n    ...\n    // optional: aocmd_file_bootcmd_exec_on_por(); \n    ...\n    aocmd_cint_prompt();\n  }\n\n  void loop() {\n    aocmd_cint_pollserial();\n    ...\n  }\n  ```\n\n- When _implementing_ a command, the signature of the command handler \n  (`aocmd_cint_func_t`) is important, as well as how to register it \n  (`aocmd_cint_register()`). In the command handler, parser routines such as \n  `aocmd_cint_parse_hex()` and `aocmd_cint_isprefix()` are helpful.\n\n\n### aocmd_echo, aocmd_help, aocmd_board, aocmd_version, aocmd_file, aocmd_osp, aocmd_said\n\nAll these modules have a function to register the command. We take \"echo\" \nas example.\n\n- `aocmd_echo_register()` registers the built-in \"echo\" command with the command interpreter.\n\nRecall that `aocmd_register()` calls all register functions in this library.\nSo, either call that one, or call a subset of `aocmd_xxx_register()`.\n\nSome of the modules require initialization like `aocmd_file_init()` and \n`aocmd_osp_init()`. This initializes the file system, respectively telegram \nparser, but these inits are called from `aocmd_init()`, so normal client code \ndoes not need to call any of them.\n\n\n### aocmd_version\n\nIn addition to `aocmd_version_register()`, there are two other public\nfunctions. These are so-called weak upcalls from the `version` command handler.\n\n- **weak** `aocmd_version_app()`; it shall print to Serial the application name and version.\n- **weak** `aocmd_version_extra()`; it may print to Serial additional ingredients with name and version.\n\n\"Weak\" means that an application can re-implement those functions \n(using the exact same name), and those re-implementations take precedence \nover these weak implementations. For details see the section\n[Upcalls via weak linking](#upcalls-via-weak-linking) below.\n\nThe default implementation of the two functions is printing a stub line \nrespectively printing nothing. \n\nThe version command already prints the version of ESP runtime, \nESP compiler, Arduino IDE, compilation date/time, and the version of\nthe lower libraries aoresult, aospi, aoosp, and aocmd.\n\n\n### aocmd_file\n\nIn addition to `aocmd_file_register()` and `aocmd_file_init()`, there is \none other public function:\n\n- `aocmd_file_bootcmd_exec_on_por()` executes the file `boot.cmd` on \n  power on reset, by feeding its content to the command interpreter.\n\n- Note that the size of `boot.cmd` is limited to 2047 including the \n  terminating 0.\n\n\n## Execution architecture\n\n### Main loop\n\nWhen the command interpreter is integrated into an Arduino application, \nit has the following form:\n\n```c++\nvoid setup() {\n  Serial.begin(115200);\n  aocmd_init(); \n}\n\nvoid loop() {\n  aocmd_cint_pollserial();\n  ...other...\n}\n```\n\n- The command interpreter uses `Serial` to receive commands and transmit answers.\n  This usage is implicit (accessing the global symbol `Serial`), so `Serial` \n  must have been `begin()`.\n\n- The command interpreter must have been initialized, this is done with \n  `aocmd_init()`.\n\n- Finally, the function `aocmd_cint_pollserial()` is the work horse.\n  It checks `Serial` for incoming characters, and passes them to the command \n  interpreter.\n  The command interpreter stores them until a CR or LF is received.\n  Then the command interpreter parses the command line (splits it in \n  arguments `argc` and `argv[]`), looks up the command name and executes \n  the associated handler.\n\n- The function `aocmd_cint_pollserial()` checks for incoming characters using \n  the `HardwareSerial` interrupt service routine. That routine picks up \n  incoming characters, and stores them in in a buffer with size \n  `HardwareSerial_rxBufferSize(256)`. \n  \n  In other words, when characters come in at a high pace there is a risk of \n  losing them (buffer overflow). To mitigate this risk, and for the liveliness \n  of the command interpreter, it is best if the execution time of `...other...`\n  is small (well below 100ms).\n  \n  Of course the sender of the  characters (the PC) should not just flood\n  the ESP with commands. Best is to send a command, wait for a new prompt\n  and only then send a new command.\n\n\n### Upcalls via weak linking\n\nThe `aocmd` uses _weak linking_ as a mechanism to let the library upcall to \nthe application.\n\nIt is used by the `version` command. It has two functions with weak linkage \n`aocmd_version_app()` and `aocmd_version_extra()`.\nThe first one is a stub printing `no application version registered`; it shall be replaced by a function in the application that\nprints to Serial the application name and version.\n\nThe second function is empty; it may be replaced by an application specific function \nthat prints to Serial additional ingredients with name and version.\n\nThe diagram below explains the behavior of the compiler and the resulting \ncall graph.\n\n![Weak upcall](extras/upcall-weak.png)\n\n\n\n## Python (experimental)\n\nThe examples directory of _aotop_ contains an Arduino sketch \n[osplink.ino](https://github.com/ams-OSRAM/OSP_aotop/tree/main/examples/osplink).\nThis is an application, that contains just the command interpreter, \nand with that the commands to send and receive telegrams.\n\nThe directory `python` in _aocmd_ contains a Python library \n(`libosplink`). This library has one object (`CmdInt` in module `cmdint.py`) \nthat can communicate with any command interpreter, and one object \n(`OSPlink` in module `osplink.py`) that can communicate to the command \ninterpreter as implemented in the Arduino `osplink` sketch.\n\nThe directory `python` also contains two Python examples,\none for each module, that demonstrate how to use those objects. \nSee the [readme](python) for instructions. Note that the Python\nlibrary is an experimental proof-of-concept.\n\n\n## Version history _aocmd_\n\n- **2025 September 17, 0.6.1**\n  - Updated otp dump example.\n  - Added link to examples.\n  - `said otp` (dump) now reports error.\n  \n- **2025 May 21, 0.6.0**\n  - Fixed bug: `file` without arg printed error with zero string.\n  - Fixed bug: crash when telegram name was unknown.\n  - Improved `osp info` text for `asktinfo` and `askvinfo`; improved payload size handling.\n  - Added mac address (as board id) to `board`.\n  \n- **2025 March 3, 0.5.9**\n  - Adapted to new AOOSP_I2CCFG_SPEED_XXX constants.\n  - `said i2c xxx write` now uses `aoosp_exec_i2cwrite8` io `aoosp_send_i2cwrite8`.\n  - Improved help on `osp fields`.\n  - Typos fixed.\n  - Fixed documentation error for `aocmd_init()`.\n\n- **2024 November 29, 0.5.8**\n  - Improved text \"help osp\".\n  - Improved `osp info` text for `readledst`; added `readledstchn`.\n  - Improved `osp info` text for `readcomst`.\n\n- **2024 November 8, 0.5.7**\n  - Improved `osp info` texts.\n  - Documentation improvements.\n  \n- **2024 October 24, 0.5.6**\n  - New command `said password` to get/set SAID password in `aoosp` store.\n\n- **2024 October 22, 0.5.5**\n  - Added command `osp aoresult [ \u003cfilter\u003e ]` to show errors (nr/id/description).\n  - Added command `osp fields \u003cdata\u003e...`, similar to python dissector.\n  - Fixed typos in python `readme.md`s.\n  \n- **2024 October 8, 0.5.4**\n  - Prefixed `modules.drawio.png` with library short name.\n  - Moved domain from `github.com/ams-OSRAM-Group` to `github.com/ams-OSRAM`.\n  - Updated readme's for Python.\n  \n- **2024 September 10, 0.5.3**\n  - Added I2C read and write commands `said i2c \u003caddr\u003e read|write` and updated `readme.md`.\n  - Added I2C frequency control `said i2c \u003caddr\u003e freq [\u003cfreq\u003e]`.\n  - Added arguments to functions in `osplink.py` and `exosplink.py`.\n  - Extra top-level readme for `python`, and update of deeper Python readme's.\n  - Added power estimate to `osp enum`.\n  - Shortened filename in `aocmd_template.ino`.\n  - Updated description of examples (BEHAVIOR section).\n  - Some `DC3` chars removed from `readme.md`.\n  \n- **2024 August 5, 0.5.2**\n  - Updated section \"Example commands\".\n  - More uniform error messages in command handlers \"'xxx' expects 'yyy', not 'zzz'\".\n\n- **2024 August 28, 0.5.1**\n  - Command `help \u003ccmd\u003e` now has topic selector.\n  - Commands `help` and `said` now support prefix-@ to suppress output.\n  - Command `osp` now prints round trip time.\n  - More uniform error messages in command handlers \"'xxx' expected 'yyy', not 'zzz'\", \"'xxx' has unknown argument ('zzz')\", and \"'xxx' has too many args\"\n  - Removed `[@]` in SYNTAX help.\n  - Added links in `readme.md` for all example sketches.\n  - Commands `osp send`, `osp tx`, and `osp trx` validate initloop/bidir telegrams against dirmux state.\n  - Updates to `readme.md` and `system.drawio.png`.\n  \n- **2024 August 9, 0.5.0**  \n  - Added comment on `F()` and `PSTR()`; `aocm_board` being ESP32S3 specific and `boot.cmd` being 2k max.\n  - Removed `docname` (aka old name or datasheet name) in `aocmd_osp.cpp`/`aocmd_osp.i`.\n  - Corrected link to GitHub from aotop to OSP_aotop.\n  - Remove \"oalib\" from `sentence=` in `library.properties`.\n  - Typos in `aocmd_cint.h` fixed\n  - Arduino name changed from `OSP Command interpreter - aocmd` to `OSP CommandInterpreter aocmd`.\n  - Renamed dir `extra` to `extras`.\n  - Added command `osp hwtest` to help testing the OSP32 board.\n  - Command `osp enum` now has summary formatted the same as `topo enum`.\n  - `license.txt` line endings changed from LF to CR+LF.\n\n- **2024 July 02, 0.4.4**  \n  - Initial release candidate.\n\n\n(end)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fams-osram%2Fosp_aocmd","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fams-osram%2Fosp_aocmd","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fams-osram%2Fosp_aocmd/lists"}