Climte-Growcontroller(touch) project: Most logic way to structure your classes? Best use of classes with esp32 + devices

Hello there together. New to Arduino and esp32.
I am Stuck with the meta, really need help. :sweat_smile:

My Goal is to make a Growcontroller with:

  • ESP32-S3-DevKitC-1-N16R8V_16MB_Flash_Quad_8MB_PSRAM_Octal
  • several SHT31 sensors(humidity/temp)
  • PWM Control for the exhaust fan(Climalogic)
  • touchdisplay:
    LAFVIN 3.5 Inch TFT LCD Touch Display Shield Module 480 x 320 SPI Serial ILI9488
    uses tft_eSPI
    Display of charts and a touch interface, to adjust some settings,....this will probably grow.

Who knows what will be added, and who knows If I fall in love with microcontroller, and do more stuff.
That's why I am thinking hard how to organize it all, be modular, and organized.

I got my first results with ArduinoIDE, than decided to go for Vs Studio, Platformio and Arduino framework. (managed to make all fully portable, without touching windows)
Not decided yet, if I want to go for ESPHome or not in the future, or something similar webbased.
I wanna try to programm something independent first, that works and runs by itself.

So far I have:

  • bought the hardware, printed a case for the tft.
  • made my esp32 working with profile:
    PSRAM=ESP32-S3-DevKitC-1-N16R8V_16MB_Flash_Quad_8MB_PSRAM_Octal
  • soldered for the first time in my life.
  • Got my first readings from SHT31 with the Rob Tillaart Library.
  • Got an external LED to blink
  • Wrote a few info and test functions related to memory, and fragmentation.
  • Functions to make the monitor output more beautiful(colors)

I am totally stuck, how to structure my overall classes. :face_with_peeking_eye:
I think important is, that it is modular, since I don't know my future path, but regarding the esp32, I think I WON'T need to switch to more powerful things.

My Main Question, what structure would be best?
(Since I know that restructuring in hinsight is torture)

Factors:

  • I know it is not THAT complicated for now, and I tend to overkill stuff,
    but I think this is good, to get back to classes and c/c++ (Havn't used it for 10+ years)
  • Not sure how heavily I will use RTOS, never worked with it.
  • Will stick with ESP32.
  • For sure will use and swap displays(type, size), even in existing projects, like my climatecontroller.
  • not sure If I use Esphome in future, likely.
  • So want to make it flexible, while focusing for now to make it run indepent first, without wifi or esphome, or webserver.

Here is the solution so far I have come up with ChatGPT.
Here is where you guys can greatly help me.
It's possible we got carried away, lost in AI so to speak. :see_no_evil:
But I prefer are more organized structure that is overkill, instead of running into problems later, when all is already advanced.

chatGPT said this solution, is flexible, and i can use a OOP approach, or RTOS, or mix.
Keeping the display in it's seperate class, helps performance with RTOS, and makes display changes easier.

I think the display(touch) with nice little charts and stuff I can come up with, is the only performance heavy thing in the whole thing, and I have no clue, howe good or bad it will be.
But also the problem is, that I am new to this stuff, and cannot plan for everything right now. Really need your help, if this all makes sense.
Just want to avoid a bad structure, because I am a noob, and need to restructure everything in the future.....puh that' a lot text, i hope someone is still with me.

My (Main Class)
│
├── Esp32 (ESP32 Class)
│   ├── GPIO (GPIO Control, Scanner, etc.)
│   ├── Memory (Memory Info, Test, etc.)
│   ├── WiFi (Wi-Fi Management)
│   └── TaskScheduler (RTOS or Task Management)
│
├── SHT31 (SHT31 Sensor Class)
│   ├── Begin() (Initialize Sensor)
│   ├── getTemperature() (Return Temperature)
│   └── getHumidity() (Return Humidity)
│
├── Display (Independent Display Class)
│   ├── TFT_eSPI or LVGL (Rendering Library)
│   ├── Screen (Screen-specific logic)
│   └── Touch (Handle Touch Input, Callbacks)
│       ├── onTouch() (Callback for Touch Events)
│       └── drawUI() (Draw User Interface)
│
├── ESPHome (Integration Layer)
│   ├── Sensors (Report sensor data to ESPHome)
│   ├── DisplayState (Update display state through ESPHome)
│   └── WiFi (Ensure stable Wi-Fi for communication)
│
└── RTOS (Real-Time Operating System Manager)
    ├── Task_SensorReading (e.g., read sensor data every 5 minutes)
    ├── Task_DisplayUpdate (e.g., update the display periodically)
    ├── Task_WiFi (Handle Wi-Fi communications)
    └── Task_ESPHomeSync (Synchronize data with ESPHome)

Here is some reasoning from chatgpt:

  • TaskScheduler (inside ESP32 class): Now handles only low-level tasks like managing hardware-related functions (e.g., GPIO control or memory management).

  • WiFi remains a part of the ESP32 class, where it handles the low-level connection management, like connecting to the network.

  • RTOS Manager (outside ESP32) handles high-level periodic tasks like reading sensor data, updating the display, and syncing with ESPHome.

  • Separation of Concerns: Keeping low-level tasks in the ESP32 class and high-level tasks in the RTOS helps maintain clarity. The ESP32 handles its hardware duties (like keeping the Wi-Fi connected), while the RTOS takes care of more application-level needs (like checking sensor data every 5 minutes).

  • Performance Considerations: This separation ensures that hardware management (inside ESP32) can operate as efficiently as possible without bogging down the system with higher-level concerns.

Sorry to say this, but it is not customary to discuss code from ChatGPT on the forum.
If you have any problems with such code, ask AI to fix it for you.

Oh hello there.
What?
I have not posted code by Chatgpt at all!??
There is no Code all in my text?
I used it to work out my overall structure.
It's just a structure tree view drawed by chatgpt.
It listed pros and cons of different structures, and I decided as best as I could on that structure.
Since I have never worked with ardunio and esp32 stuff, I cannot judge, if my overall plan is sound.
I thought this would be the right place to ask,..arduino forum, since you guys have experience with exactly that. I don't.
I mean nothing changes, if I delete any mentioning of it at all :wink:

If you would ask me - surely, you haven't.
The good rule - do not ask an AI to do something that you don't make yourself. The Ai should be an instrument to improve your probability rather than replacement of the knowledge and learning.

If you are interested in the opinion of other participants, read this thread:

that's something that comes with experience, seeing how other projects are organized or poorly organzied and having to maintain them.

doubt such a project needs an OS (which are more of social management tools, i.e. individual sandboxes). OSes handle the asynchronous needs of drastically different types of applications

Cargill discusses classes, poorly designed classes, their flaws and how to improve them.

Ok I understand you now, did not know, that topic is so toxic. But not really wondering.
I used several AIs, to get different approaches, since google did not really help here, how the overall structure would be best. I have not found any post, that discusses the meta structure, maybe I lack search terms.
I have gone back and forth 1000 times with them, and always asked and try to understand their reason for doing things.
The only problem I have, I don'T know how these components best play together.
Just want to have a good groundwork, even if it is overkill.(I wanna learn on the way a bit more than I need to)

Yes you are right. I had the same concern. I can also swap this with a task manger that manges task, and not a full HAL or OS. I heard and read on many places, that I probably will have to use RTOS anyway, not sure how heavy. (touch display)

But basically a class, that manages the overall stuff, like a taskmanager, where I decide if I use RTOs or not.

I am leaning towards a different solution:
MY (Main Class)
├── ESP32Manager
│ ├── GPIO
│ ├── Memory
│ ├── PWMController
│ └── LowLevelTasks
├── SensorManager
│ ├── SHT31Sensor1
│ ├── SHT31Sensor2
│ └── FutureSensors...
│ └── ReadSensors() (Handles reading data from all sensors)
├── DisplaySystem
│ ├── TFTDriver (using TFT_eSPI)
│ ├── UIManager
│ └── TouchController
├── ClimateController
│ ├── TemperatureControl
│ ├── HumidityControl
│ └── FanControl
├── DataManager
│ ├── SensorDataProcessor (Processes raw data from SensorManager)
│ ├── DataLogger (Handles logging of sensor data)
│ └── DataVisualizer (Formats data for display)
├── TaskManager
│ ├── SensorReadingTask (Calls SensorManager::ReadSensors)
│ ├── DisplayUpdateTask
│ ├── ClimateControlTask
│ └── DataManagementTask (Handles data logging and processing)
└── CommunicationManager
├── WiFiManager
├── ESPHomeInterface (for future use)
└── WebServerManager (for future use)

Would this be a valid approach? I will not use all (at the start), but I just want a blueprint, where I can add and change(eg. Display) easily.

I think the only question left, if display, wifi and sensors would make sense in their own class, or if they are better to be fitted as a subclass of esp32.
Or if my questions are totally wrong, and my focus should be somewhere else...lol.

It makes no since to add the extra layer of a "task manager" since FreeRTOS is always running on ESP32 anyway. It's a highly capable task manager and more. But, using it (or any multi-tasking environment) requires you to think differently about how the program is structured, how the tasks interact, and how they share data. For my ESP32 projects that warrant it, that's the paradigm that I prefer. But, there's absolutely no reason you can't implementing your entire project using the standard "Arduino Multitasking" techniques of millis()-based timing, state machines, etc. On ESP32, this means your entire code will run within one FreeRTOS task called loopTask().

The topic of task management may be related to how you abstract the physical recourses of your project into classes. But, it's also different. You seem to be confounding the two.

don't understand what this list/description means.

presuambly the code needs to

  • repeatedly monitor inputs: either sensors or user request thru one or more interfaces (tft, bluetooth, web)
  • perform some processing of the input: interpreting sensor data or servicing user requests
  • possibly perform some periodic actions such as temperature control

a common problem seen on this forum is newbies writing a ton of code and then lost when it doesn't work

suggest you start with developing output functions, then verify the interfaces to hardware, starting with the user interfaces which can then be used for testing, and then sensor and control hardware. finally the autonmous mechanisms

develop one new feature at a time before moving to the next feature

include test/diagnotic/logging capabilities from the begining

OO doesn't require the use of classes, but it makes sense to separate functionality into separate files such that each file supports one purpose and minimizes and interdependencies

Thanks alot gfvalvo.
That's exactly the point, I don't know enough, to judge what is needed or not.
Getting a sensor reading with RTOS every x time is not difficult.
But worried about the display with touch and RTOS, since this will be more difficult.

Ok not sure if I will use a real RTOS implementation, or this one FreeRTOS task called looptask(), but if that requires fundamental changes in the structure, that's why I am here to find out, BEFORE I start.
But when you say it's not needed(probably gets very complicated later on),
I trust you on that, since you have already done several projects.

But then I would just change my TaskManager Class into a looptask() manager Class so to speak, keeping the organized structure, and be open for changes later on.

Or would my fundamental structure need to change with the looptask approach?

I just want to avoid going down a total wrong path at the start, and have a template, that is most flexible, even when there is obviously overhead that is not really neccessary.
But better this way, than the other way around.

And with an ESP32-S3-DevKitC-1-N16R8V_16MB_Flash_Quad_8MB_PSRAM_Octal I should not worry about overhead that much, or do I? :wink:

I think you're over-thinking. As already suggested, start with getting the various smaller parts of the project working independently. Don't rely on the delay() function to implement any required timing. Once done, you can tackle how to integrate them.

Thx for the answer.
I don't have a user interface yet, have not even started with the display.(touch)
This one is probably the toughest of them all, and I heard RTOS( or a single taskloop() as gfvalvo said) is probably needed to make it smooth.
Someone else said, an event base structure is the best approach, with callbacks and co.
(not really sure what he means, different than RTOS?)

Of course I won't implement all at first. I skip the display, and just want to make it work.
Getting sensor readings and pwm control is not really hard, just the display with touch is the biggest unknown.
I am more the methodical and overkill type, thats's why I wrote alraedy different memory functions, to learn my esp32 better, get back into C/C++ and programming.
I played around with it for 3 days, BEFORE I attempted my first sensor reading or blink example.
The thing is....I have written a lot of functions now, like my_esp32_Memory_test_full and so on, and I realized I need to think about structure BEFORE it gets wilder and I really start.
That's why I am here.

Just want to order stuff, so I stay flexible and can experiment, what I really like and need.
And this blueprint is maybe overkill, but getting my first reading by a task, sending it to a different module for processing and so on, makes sense to me, and can only help in the long run.
Maybe I am mistaken.

Would I run into trouble with that in thje long run?
What would you change?

No I learned to avoid delay on day one :wink: (since it's obviously a blocking operation)
I prefer overthinking at the start, than to run into roadblocks later.

Like putting The display and sensors in their own class for example, cannot be really bad, can it?
Much easier life if they are a subclass of esp32?(Does it matter at all?) Does the choice of RTOS or taskloop() change any of that?

not sure what you mean by structure. not sure if by structure you mean class which are synonymous, and not much different that what i said about separating functionality into files that have well defined purposes.

i think you may be asking about architecture -- how the pieces fit together

without much experience -- learning from the school of hard knocks -- you'll need to be prepared to redesign things. Isolating functionality into files means the implementation can be changed without affecting others

one thought is have a common interfaces. For example input from a interface is translate into a form, if necessary, to be processed by a single routine. Also a similar approach for output, strings that can be output to the serial monitor, a display or posted on a web page

Deciding wheater to go FreeRTOS or Aduino:
You said you prefer to use it, when projects are more difficult?
Without touch, my project would be extremely simple.
But who knows how complex my UI and graphics will become?(rhetorical question)

Is RTOS really that much more challenging?
Since I am new to both approaches, I cannot judge.
But you obviously DON'T use it for all projects, even giving the fact, that you ARE familiar with it.(Most important sentence of yours)

When my project grows, that single taskloop() sure will become complex.
On the other hand, FreeRTOS will get complicated when their are bugs and stuff not working, because of the complexity in the "background".

So that means, if I read you correctly, using RTOS is a bit painfull, and you use it only when really needed, and you think I don't really need it?

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.