Post History
If it works, you understand the tradeoffs, and you feel it is maintainable, then it's not wrong. However, I wouldn't and don't do it this way personally. I have done many microcontroller projects...
#1: Initial revision
If it works, you understand the tradeoffs, and you feel it is maintainable, then it's not wrong. However, I wouldn't and don't do it this way personally. I have done many microcontroller projects. The vast majority of those communicate to a host over a UART, even if just during development, production test, or field diagnostics. What I have converged on is a simple binary protocol. Data is sent in both directions in packets that start with an opcode byte and are followed by whatever data bytes are specified for that opcode. For documentation clarity, I call the packets from the host to the microcontroller "commands", and from the micro to the host "responses". However, responses are not always in direct response to commands, and can be sent asynchronously. There are a number of advantages to this scheme:<ol> <li>It is simple to parse. You don't have to look for CR, LF, maybe NULL, and possibly other control characters. When the micro is ready for the next command, it interprets the next byte as a command opcode. <li>It is simple to interpret. The "command name" is a single byte. The command opcode value is indexed into a dispatch table to run the routine to process the particular command. Each command routine knows what data bytes, if any, to expect. <li>It's fast. In your example, turning on a relay would take two bytes. The first is the "relay on" command opcode, and the second the relay number. <li>No ASCII to binary conversion is required. The micro will ultimately use data in binary. Sending it that way makes it much easier on the micro. <li>Using this structure consistently allows for canned routines to re-use between projects, both in the micro and on the host. For example, I have canned routines that receive 1, 2, 3, and 4 byte values into registers in the micro. </ol> Unfortunately much of my code is for specific customers and therefore private. I can show you some general "library" routines I have around this concept, and an example of complete firmware for a PIC 18F2550. See the GitHub project RDY2T at https://github.com/EmbedInc/rdy2t. This one happens to communicate to the host via USB instead of a UART, but the command processing logic is identical. USB endpoint 1 is used as bi-directional streams of bytes, which is exactly what a UART provides. See the "doc.txt" file. That's the documentation file for this firmware. Every firmware I create has one of these. One part of every firmware doc file describes the external protocol, if any. See the sections "USB Protocol", "Commands", and "Responses". I include commands NOP, PING, and FWINFO in pretty much every firmware. The remaining commands and responses depend on whatever the firmware does. In this case (RDY2T), it is meant to be example and template firmware for my ReadyBoard-02. The command reception and dispatching is handled in the CMDUSB module, rdy2t_cmdusb.aspic. Note that all the logic is actually in the CMDUSB.INS.ASPIC file in the PIC repository (also available on GitHub). There is no need to re-write that every PIC project. The actual commands are in the CMD module. Since I do this pretty much every PIC project, I have created "library" facilities to make it easy each instance. The commands and response for the RDY2T firmware are defined in the RDY2T_CMDRSP.INS.ASPIC include file. This keeps the definition of all command and response opcodes in one place. This is the only place where the mapping from command/response mnemonics to actual opcodes is defined. This may look confusing because it heavily uses my PIC preprocessor, PREPIC, but you should be able to follow the comments. The preprocessor state left around from processing this include file is used in the CMDUSB module to automatically populate the dispatch table. That's how it can be all "library" code without needing modification to the specific set of commands. The CMDRSP.INS.ASPIC file is also used to create constant symbols for the host source code. The host code only refers to command and response opcodes by these constants, and not by their actual numbers. Reassigning a command or response to a different opcode only requires changing the CMDRSP.INS.ASPIC include file, then rebuilding the firmware and host software. There are similar facilities for dsPICs in my DSPIC repository at https://github.com/EmbedInc/dspic. These are all open source and free to use with very minimal strings attached.
