“While there are a lot of #ifdef’s, I don’t think that is what will make splitting things hard” — that’s not what I meant — what I meant was that those ifdefs are what’s making the file large.
What you mentioned about creating abstract classes is what we did for classes like GPIO.h/.cpp. On the other hand, main.cpp is based and evolved from legacy code (OpenSprinkler 1.0) for which I had to do a lot of tricks to maximally save flash memory space and RAM. That’s why the code there is largely standalone functions with no classes. Later as we moved on to 2-nd generation and 3-rd generation, the microcontrollers have a lot more flash memory and RAM, so some of the new classes created can use more OOP features.