From Arduino To Automotive: How I Escaped The IDE And Owned The Bus
DEV Community

From Arduino To Automotive: How I Escaped The IDE And Owned The Bus

Arduino taught me how to build. Bare metal taught me how the build actually works. I have a lot of respect for Arduino. I mean that sincerely. It is a legitimate engineering platform, not a toy. It put a C compiler, a bootloader, and a sane hardware abstraction layer into the hands of millions of people and said go make something. Museums run on Arduino. Satellites have run on Arduino. My first three products that actually made money ran on Arduino. It is also a ceiling. And that is not an insult. Every good abstraction is a ceiling by design. The question is whether you have hit it yet. I hit it when I tried to make an ESP32 do something timing critical while also staying connected to WiFi. And that moment pushed me all the way from the IDE to the CAN bus. Arduino Is A Great Place To Start And A Hard Place To Stay Here is what Arduino gets right that almost no one else did. It solved distribution. You install the IDE, you pick your board, you press upload. You do not need to fight OpenOCD, or figure out why your toolchain is building for the wrong architecture, or learn what a linker script does on day one. It solved documentation by giving you functions that read like English. analogRead() does what it says. Wire.begin() does what you expect. It also solved community. When you get stuck, someone else has been stuck there before and left a forum post. For prototyping, for education, for one off installations, for a huge number of commercial products, it is absolutely the right tool. It is stable, it is well tested, and the core libraries for AVR and ESP32 and RP2040 are written by people who are better at driver development than most of us will ever be. But Arduino makes a trade to get that simplicity. It hides the real time operating system underneath. On ESP32, you are always running FreeRTOS whether you ask for it or not. Arduino just puts your setup() and loop() inside a task for you and quietly creates another task for WiFi and networking. That is elegant until you need to control those tasks. My project needed to sample a sensor at 20kHz with consistent timing, write to SD, and keep a WebSocket alive. In Arduino land, my options were delay() and millis() and hoping. The jitter was terrible. The WiFi would drop when I blocked for too long. The watchdog would bite. I was not fighting my code. I was fighting the assumptions baked into the framework. I did not leave Arduino because it is bad. I left because I wanted to see the whole machine. I wanted to see what was under loop() . What Lives Under loop() If you open the ESP32 Technical Reference Manual, it is over 600 pages. It is not scary. It is just thorough. It describes the actual chip. Two Xtensa LX6 cores at 240MHz, an interrupt matrix that lets you route almost any peripheral to almost any interrupt, hardware timers, DMA engines, eFuses, clock trees that let you gate power to entire subsystems. Arduino gives you a friendly front door to that building. Bare metal means you get the master key. Going bare metal on ESP32 does not mean writing everything in assembly. It means using ESP-IDF directly, owning the boot process, and understanding that your application starts long before app_main() . It means you write the linker script that decides where your code lives in flash versus RAM. It means you configure the interrupt allocator yourself. It means you decide which core does what. The first time you do it, nothing boots. You get a Guru Meditation Error and a register dump. The second time, you get a blinking LED but it is your blinking LED. You wrote the GPIO muxing. You set the clock source. You cleared the interrupt. Then you start to get superpowers. You can get sub microsecond timing because you are not going through layers of abstraction that check if the pin is valid on every call. You can pin WiFi to core 0 and your real time loop to core 1 and they actually stay out of each other's way. You can tell the brownout detector and the task watchdog exactly what you are doing so they stop resetting you for being busy. Your binary goes from 800KB to 80KB because you only linked what you use. It is not that Arduino cannot do this. It is that when you need this level of control, it is easier to work with the silicon directly than to fight an abstraction that was trying to protect you. That transition, from friendly wrappers to direct register control, is the foundation for everything that came after. Once you can command one microcontroller completely, you start noticing how chatty microcontrollers are with each other. Especially the ones in your car. Your Car Is Not A Car. It Is A Network With Cupholders Every modern vehicle is a distributed system. Depending on who counts, your average new car has between 40 and 100 ECUs. Engine, transmission, brakes, steering, airbags, doors, instrument cluster, infotainment, all of them are computers. They talk over CAN bus. Controller Area Network. Two wires, CAN High and CAN Low, twisted together. It was designed by Bosch in the 80s for reliability in noisy environments. It is brilliantly robust. It is also completely trusting. CAN has no authentication. No encryption. No source address validation in the base protocol. Any node can send a frame with any ID, and every other node will believe it. Arbitration is handled by ID priority. Lower ID means higher priority. That is the only security model. When I connected an ESP32 and a $3 transceiver to a bench setup, not even a real car at first, just a salvaged instrument cluster and a body control module from a junkyard, and saw the traffic, it was like hearing a building talk to itself. Hundreds of frames per second. RPM, wheel speeds, steering angle, door switches, seatbelt status, all in plaintext. Reverse engineering CAN is not magic. It is patience and method. You log traffic at rest. You log traffic while you change one thing. You open the driver door, what changed? You press the brake, what new frame appears? You turn the steering wheel two degrees left, which byte increments? Over time you build a dictionary. The community calls these DBC files, and building one from scratch for an unknown car is one of the most satisfying puzzles in hardware hacking. And once you understand the dictionary, you can speak. You can send a frame that says the car is doing 60 mph while it is sitting on your bench. You can make the cluster think the doors are locked. You can replay a captured frame and watch the system respond. This is why we do it on the bench, on our own hardware, in a lab. Because the bus does not know the difference between a real ECU and your ESP32 if you send the right ID. This is the skill that took me from embedded to automotive. It is not about breaking cars. It is about understanding that cars are not mechanical objects anymore. They are networks, and if you understand networks, you can audit them, test them, and build better tools for them. The Firmware Is The Truth An ECU is just a box that runs firmware. Same with a smart lock, a router, a drone. The hardware is interesting. The firmware is the truth. It has the keys, the logic, the hardcoded credentials someone left in there in 2017, the CAN message map. Getting that firmware out is a skill in itself. People talk about firmware dumping like it is always destructive. Lift the chip, put it in a programmer, hope you do not rip pads. Sometimes you have to do that. Most of the time you do not. Most devices leave the debug interface there. Sometimes under a test pad labeled SWDIO that is covered in solder mask. Sometimes via a bootloader that will happily dump flash over UART if you hold a pin low at reset. Sometimes the read protection that was supposed to be enabled was never actually fused in production. On STM32, RDP level 1 looks scary until you understand how the option bytes work. On nRF52, APPROTECT has a recovery sequence. On ESP32, flash encryption is powerful, but it is optional, and a huge percentage of shipped devices never turn it on. Bricking is usually not about the chip being fragile. It is about sequence. You erased before you saved the option bytes. You overwrote the bootloader that contained the recovery logic. You did not dump the calibration data that lives in the last sector. I started logging every successful dump. What probe, what software, what wiring, what voltage, what failed first. What that unmarked 6 pad footprint actually was. That log became a catalog. Not exploits, but procedures. How to get the firmware out alive so you can actually learn from it. Why This Path Matters None of this is about saying one platform is better than another. Arduino is legitimate. ESP-IDF is legitimate. Bare metal register poking is legitimate. They are different levels of abstraction for different jobs. The reason to learn to move between them is freedom. When you can move between them, you can prototype in Arduino on Monday because it is fast, and then drop down to IDF on Wednesday because you need the performance, and then open the reference manual on Friday because you need to understand why the I2S DMA is glitching. You are not locked out of any floor of the building. And when you can move from microcontroller to car network to firmware, you start to see systems as they actually are. Not as products with marketing stories, but as networks of computers that can be observed, understood, and improved. I still use Arduino when it is the right tool. I also now have the option not to. That option is worth everything. If you are feeling that tug, that sense that loop() is great but you want to see what is before and after it, follow it. Read the manual. Dump a board you own. Listen to a bus that is already talking in your garage. The tools are cheap. The knowledge is out there. You just have to decide you want to own the bus, not just ride it. The Toolkit If you want to go deeper on the exact path above, these are the guides I wrote from my own notes. Each one is pay what you want, so grab them for $

Read on DEV Community ↗ ← Back to News

Comments

No comments yet. Start the discussion.