EEVblog Electronics Community Forum
Products => Computers => Programming => Topic started by: elektronchika on November 10, 2020, 02:01:48 pm
-
I'm trying to figure out what concepts to use for a new project written in C for microcontroller. The idea is to create something like a smartwatch with a BLE communication with a phone. There is a display and 4 buttons that interact with the display (up, down, select and back buttons). The main screen will show the time. Then there is the main menu where are the settings, notifications history, alarms, timer and stopwatch, watchfaces, maybe weather. All previously listed are like apps and in the watchfaces app there is a list of different ... watchfaces. On top of that notifications like phone calls and messages should appear on full screen.
So far I have some routines to write to the display and interrupt driven reading of the buttons. Some basic BLE communication is established. Here come my questions and problems.
I read a lot in internet and it appears like the best approach is to do the system event driven. I read about the active objects, timer events, publish-subscribe approach, hierarchical state machines. And still as a hardware engineer with just few firmware projects behind me I have problems understanding how to do it. There should be a windows stack, layers for text and bitmaps, buttons events, time events, ble events, etc. and each app should subscribe to the events it wants to process and have it's own event loop for processing the events. This is the highest level.
Then in the lower level there should be APIs for writing to the display, etc.
Could you explain me how it's done, because all those things are in use for decades and I certainly cannot reinvent the wheel. What should be the layers of the firmware, how to do the user interface in details. I presume there are many ways to do it, but what is the mostly used and the best to use? I tried to search for books that explain how guis in embedded systems are done, but without luck (maybe I didn't search enough). And by the way I do this as a hobby project, because I want to improve me firmware programming skills.
Thank you! Lazar
-
No replies so far, probably because this is both a can of worms and a bottomless pit :)
Not sure how au fait you are with this stuff, but very simplistically you are aiming to split the system up into a number of small parts, each of which does one thing without reference to anything else. Vertically, you'll split the system into functions: display, BLE, Ethernet, etc. Each of those would be a task, and whilst you might well split them up further internally, this is your system view of things.
[attach=1]
Typically, you will arrange for each task to run in turn. That might be via a higher-level loop that just calls each task function in sequence, or a multitasker that does the same thing but in a cleverer way.
Horizontally, you'll split things up into layers too: hardware drivers, application interface, etc.
[attach=2]
Generally, data and control only goes up and down. For instance, if your get an Ethernet issue that needs to display something, the Ethernet task wouldn't ask the Display task to show it. Instead, it will signal the application layer that there is some issue, and the application layer would figure it needs displaying so ask the Display task to display something. But it could just as easily figure it needs to be sent to a phone app and get the BLE task to handle it instead.
With these example tasks it's fairly clear what each does. That may not always be the case, but partitioning up the system will have a large effect later (good or bad) and can be very difficult to change the longer it goes on. Worth putting some effort into it, and modelling the system in the various charting disciplines helps sort it out before you write any code and set it in stone.
You'll basically do similar decomposition within each task, so for the BLE task you might encapsulate application data into 20-byte packets that can be sent via a BLE characteristic, and have some means to handshake with the other end so you can exchange multiple packets, and then a driver to actually get that raw data into the bluetooth chip. These are all more horizontal layers in that vertical BLE task and, again, stuff goes up and down rather than across. Your aim here is to implement that with functions that do simple things well, and that don't have a bird's-eye view of things - they know nothing about anything except what they're explicitly told.
Event-driven programming is probably worth a separate post. There have been discussions of it hereabouts recently I think, but perhaps someone will offer a quick precis.
-
Are you inventing your own new operating system to do all this?
First, I recommend that you have a read through Android's Wearable OS, which is designed to run on their smart watches. It should give you an insight on how events and notifications, etc, are handled.
Start here: https://developer.android.com/training/wearables/
-
No replies so far, probably because this is both a can of worms and a bottomless pit :)
no actually, its just its asked in the wrong forum... to get a good picture you may want to read and exercise something like that being taught in Windows++ by Paul DiLascia (https://www.amazon.com/exec/obidos/ASIN/020160891X/002-5253713-4241049) (its an old book that i only know. maybe there are better materials nowadays but i dont even know which one to recommend) i've done a fair amount of time in the past building MFC like structure based on lesson from the book. i believe anything Event Driven today such as QtMainWindow etc et al will somehow resemble this basic idea in their inner construction. it start with main() deep buried down inside that you shouldnt bother calling. and then there's HAL and driver, the downer to bare metal machine dependent, interrupts handling and then coupled tightly (or loosely depending on your requirements) up to the higher level event driven structure (not covered in the book though you have to find your own materials).
yes we are talking about reinventing the wheel of a quite complex system here but what really bothers me is no high level structure like this is explained adequately enough in a book or proper order and chapters from basic to advance (good documentation, working examples etc), so we can use them comfortably. i only have a good Qt4 book but then the new Qt5 i havent seen a good book explaining it, Qt4 method is pretty much broken in Qt5 structure (no Arthur), so my exercises in learning process is interrupted. good thing i see in Qt5 is that it now support ARMv7 and Android toochain? i've downloaded some GUI library for embedded ARM/Arduino etc, but lacking book and explanation make the enthusiasm go down to 0% i thought maybe its still better to make my own. :-\
i've made an attempt for Arduino MEGA system in the past https://www.eevblog.com/forum/microcontrollers/standardized-(nicely-organized)-embedded-system-programming-libraryapi/ (https://www.eevblog.com/forum/microcontrollers/standardized-(nicely-organized)-embedded-system-programming-libraryapi/), but its faaaaarrrr from completion since halfway i figured its not worth developing for AVR system, i better move on to ARM core but never got a chance until today..
-
you may want to read and exercise something like that being taught in Windows++ by Paul DiLascia
While I agree that writing for Windows (or WearOS) will allow one to gain the knowledge via osmosis, the spec here is C on a microcontroller, which implies getting under the bonnet. The problem with the approach of writing for an existing solution is that you're writing for that solution and don't necessarily understand why you do certain things, other than because that's how it has to be done for that solution. It also doesn't teach you how to design the system, because it's already been done by someone else.
Further, I would suggest that learning via an implemented GUI can lead to bad habits. If you have a button that causes something to happen, 90% of new GUI programmers will attach the 'happen' function to the button event handler.
-
Hey, thanks for the replies. My similar post in stackoverflow was deleted because it was very generic.
I definitely don't want to re-invent the RTOS and BLE stacks.I plan on using something like FreeRTOS or qpc framework (https://www.state-machine.com/qpc/ (https://www.state-machine.com/qpc/)) and the BLE stack from manufacturer of the BLE chip. I want to focus on the graphical library and the application layer. There are a lot of GUI libraries that are free to use for non commercial suppose, but they are much more complex from what I need. I will also learn more by writing my own. This is my idea so far.
Cheers,
Lazar
-
I would personally use a finite state machine to represent the user interface. I have shown examples of the structures I'd use here (https://www.eevblog.com/forum/programming/simple-menu-system-with-dynamic-keypad-functions-(embedded-alphanumeric-display)/msg2860070/#msg2860070), noting that I tend to conserve RAM use by putting such structures in ROM/Flash.
I've looked at EastRising/BuyDisplay 1.28" round TFT modules with 240×240-pixel IPS panels; they're 36mm in diameter physically, with a 32.4mm diameter round display area (188 pixels per inch). The GC9A01 driver chip is controlled using 4-wire SPI (data, clock, chip select, and data/command select), much like ILI9341 etc. While you'll need to write your own "driver" for it, it is very similar to existing drivers. Example code is provided only for 8051-based microcontrollers, but it should not be too difficult to adopt to ones own needs.
Assuming you have sufficient amounts of ROM/Flash, you don't actually need a "GUI library" per se, just a flexible blitter. All your letters and digits are just rectangular (packed) pixel maps, with an alpha channel (for maximum image quality); or with a palette (indexed color) where each color also includes opacity/alpha.
When the display is updated, you construct a block of pixels (typically one or more rows), initialize it with the correct part of the dial image, then render or blit the relevant parts of visible digits and overlays on top, in visibility order. Unfortunately, I am not aware of any existing MCU graphics library that was optimized for this; however, I haven't looked too hard, since I happen to enjoy that sort of programming myself.
In practice, instead of maintaining an "image" of the display, you maintain data structures (lists or arrays) that describe what is drawn where. (For text, you might use a different set of structures, with a reference to the pixmap set (font) to be used, and construct the pixmap at rendering/blitting time. For analog clock hands, you might use polygons, and so on.) Whenever these structures are changed, the display needs updating. If the structures also contain their bounding box in pixels, the regions that need updating are the union of added and removed bounding boxes.
Since the display is the key to the project, I would definitely start by implementing the display standalone, with whatever microcontroller I prefer. Or heck, even write a trivial simulator in C, and experiment with it. The point is, start drawing actual pictures of what you'd actually want to see on that small display. When you know what and how to display the stuff, you can think about the overall structure of the code.