Part 1
This tankbot platform stabilization platform serves as the base for open liquid delivery through rough terrain and natrual disasters. Embedded System Design, Micro-Controller OptimizationThe Controller takes inspiration from an early GameBoy design including the full usage of C bare-metal programming. In the spirit of complete optimization of an embedded system there is not OOP C++ code within the controller project where all the code acts as Psuedo-Objects utilizing Structs for variable management, and functions with self * (pointers). My Custom Architecture Advantages:Ability to implement design patterns (Adopters, Observer(Pub/Sub), FlyWheel)Publish Subscribe / Observer C functional programmed modules can be added to the architecture while maintaining: loose couplingminimizing code dependanciessubscribing to test-driven development with Unit tests for abstract featuresOptimized Memory Access even though structs and classes have the same memory size structs will be executed faster and safer due to not being added to the heap and fully stack basedSprites do not have full pixel buffers, and are rendered directly from their sparse matrix smaller memory footprint counter partSprites stored directly to the EEPROM saving over 5 KB from the total 32 KB allowable on the Parallax Propeller BoardBranchless Programming dersignConstant time conditionals are enforced by switching out IF/Switch statement logics for a mathematical operations when possible maximizing performanceEx. Functions written as a MACRO functions #define CLAMP_JOYSTICK(a) (MAX(MIN(a, MAX_JOYSTICK), MIN_JOYSTICK))Repeatable C functions and structuresMacro functions were implemented to replace C++ templatesTo reuse core functionality in Pseudo-Objects, composition was heavily used and inspired by the Flywheel design pattern-Ex. Publishers all hold a local TankStatus so they can memcopy() their data directly to the subscriber pointer listEx. The graphics mapping based on the Subscriber Status, reused the same structure from holding sprite EEPROM addresses, SparseMatrix lengths, and subscriber struct pointerStreamed byte sequences that directly translate to the publisher/subscriber modelThe structure used for Psuedo-Objects to communicate within the Pub/Sub architecture will pack itself and unpack itself to a byte sequence so other boards not on the same stack can read and communicate with the controllerDeclaration Pattern using Headers and Source files seperated by directoryMakefile for running tests, terminal Graphics simulators, compilation, and flashing. Minimizating cross machine build dependancy issuesMy Custom Architecture Constraints:Complexity of Development Lost organization of encapsulationNo NamespacesEnforcement of unique naming conventionsDynamic Memory WorkaroundsWhile more of a hardware constraint with the FlexSpin compiler used, Malloc() and Free() could not be used as they costed too much SRAM utilization for the Parallax Propeller Board Stack called buffers needed to be pre-allocated in the main() loop and assigned to Struct Pointers that would act as workarounds for dyanmic memory
Part 2
Overview
This addition of TankBot takes the lowlevel protocol developed in the last micro-controller hc05 iteration of TankBot, which I call TankStatus (8 Byte protocol), and ports it over to python, C#, and C++. Using this protocol I developed multiple container microservices to interact together as a docker compose cluster connecting an Http server, WebSocket Proxy server, cloudflare tunnel, and locally run Ollama LLM network for tool calling.The HTTP Server hosts a Godot Engine-generated WebGL html5 3d graphic of the TankBot with WebSocket telemetry for interfacing with the physical hardware via Chatting and and Direct Drive. I also mocked up some small HTML files for reading telemetry values and direct driving the motors via the browser.Development/DevOpsAs the main hardware for controlling/reading the Arduino boards was the Nvidia Jetson Nano, most of the development happened over SSH. The Jetson ran on a NixOS implementation of NVIDIA JetPack so I added my custom NeoVim extension (forked from LazyVim) https://github.com/dogketchup/LazyVimConfig so it would be easier to program directly in the Jetson. As I also had teammates focusing on the platform stabilization aspect I added extra my personal WireGuard VPN client to the NixOS configuration stack and created Nginx reverse proxy stream so that they could SSH into the robot to run scripts as well.I also added an Arduino makefile that would operated as a flashing script using the arduino-cli to compile and upload. This was inspired by flashing mainline Klipper on my Sovol SV08 printer where the main mother board had a an STM32 Serial flashing script to talk to its MPU boards. In my situation I had my python script communicate via the same C++ library as the arduino)For testing prototype ideas/unit testing I added a Jupyterlab notebook into the cluster so that I could verify serial port and nvidia jetson GPU passthrough, Ollama endpoints, proxy Websocket Server Gateway endpoints, and OpenCV GPU usage (used NVIDIAl4t docker image image)For building the Containers, I use MSbuild for linking the .so TankStatus.h custom library and building a Docker Arch Image hosting my Dotnet landing page server and use docker compose for deploying and building all the other Micro Services.For testing I would use my NixOS server (architecture x86_64 not Jetson's aarch64) so I've got a psuedo macro in one of my dockerfiles that picks which Nvidia Jetson CUDA image to use depending on the system architecture, with minimal re-configuring the image.
Micro-service Stack and User Operation
The user endpoint for interacting with the robot exist within a a Dotnet container that tracks a local TankStatus Object where REST API updates the variable directly. For Post I went fancy and had its endpoint receive the TankStatus byte sequence, but for getting/poling that data I converted it to JSON as by that point I was spending too much time debugging and removing automating byte padding and figured it would be easier to integrate the wed front-end applications to listen and write JSON data. Once the user uses one of the multiple drive/chat interfaces the user's input is forwarded via WebSocket connection to a WebSocketGatway Python FastAPI server that has a linked in pybind implementation of the TankStatus Library that it then fowards via USB/USART to the arduino. For posting tankbot's telemetry stream two if statements operate within an async loop(asyincio with FastAPI) and if bot if statements trigger via the Tankstatus change flag the data from the Ardunio Uno. The flag was an unintentional by product of the Parallax graphics controller 1st iteration of TankBot, used for the Publish/Subsrcibe C arcitecture, so it was cool to be able to use it functionally the same here. Once both TankStatus Serial polls have their flags triggered, the concatenated tankstatus is forwarded to the dotnet server's POST tankStatus endpoint where the byte sequence is directly updated to the server. For accessing this data externally, a GET endpoint forwards the global variable and converts it to JSON data; read by the calling front ends (being the html and Godot WebGL)When a socket connection is made to the WebSocketGateway's endpoint, ws/drive. The JSON data holding two integers(soon to be converted to two 1 byte unsigned chars) adds the drive speeds(defaulted to 0 and 255 until I make a joystick frontend) and writes that serial data to the Arduino. My teammate wrote a ramp up function that slowly moves up to the desired PWM signal which was used for simplifying the platform control system. Direction is handled on the arduino's endWhen a socket connection is made to the WebSocketGateway's endpoint, ws/chat. the prompt is packaged with an optional tank move function with the arguments (left PWM, right PWM, and duration) where the LLM determines what amount of values are needed to drive the TankBot. The Python Tank_Move function will trigger and generate the relevant TankStatus Library byte sequence Packet and stream that to the Arduino via PySerial. Then after an async delay (The tank move tool still exists with the FastAPI server) a neutral 127,127 packet is automatically triggered automatically without LLM intervention. The same movement function is used for drive and and chat, but the when drive calls the function the sleep delay is ignored.For internal messaging the host.docker domain is used, but for web application front end the actual cloud flare tunnelroute domain name is used. I used Zero-Trust as it gave secure https protocol capabilities the all the web applications so that the browser's would function nicely with the services. In the future I will integrate CertBot into the cluster.
Embedded Control System
While not what I was responsible for developing I still was involved in this part of the logic flow for trouble shooting and integrating my library into the embedded stack. Th two axis system utilizes two PID loops loops decoupled by an angle transformation and running the same gains as each-other. The Platform's control loop read in the IMU's readings via I2C, filter the gyroscopic and accelerometer data via complimentary filter(low pass and high pass filter for trusting IMU values), integrate respective values for angular velocities to angles, compensate and then stream to the Serial port via the TankStatus library forced to push exactly 8 bytes with no padding. The Arduino Mega running the motor control was supposed to directly stream I2C values from added on AS6500 magnetic encoders connected via additional gears we added to the design, but the splitter we were using to read the data was not functioning correctly and we ran out of time too fully integrate it into the motor control's output Tankstatus stream. To prove telemetry output capabilities I have the Mega stream the same tankstatus it received in back to the same output functionally providing odometery of an open loop drive system.
Documentation
The system diagrams used here are PlantUml diagram generated from the Google Gemeni LLM where it generated PlantUML config files that I would edit and create graphics for in the PlantUML server. Other projects I've developed with I added the PlantUml java into the CI/CD pipeline via Github Actions.

Log in or sign up for Devpost to join the conversation.