Articles Hierarchy

Articles Home » Arduino Projects » Raspberry Pi PICO C++ code projects

Raspberry Pi PICO C++ code projects

we come from Raspberry Pi PICO intro
and here i want show the projects i tried,
possibly just test something or do ports from old Arduino projects.

Environment:
i use:
++ a medium power win 10 PC
++ in browser do this BLOG work
++ with VNC connect to my ( headless ) Raspberry PI 4 B
++ there run the VSC ( Visual Studio Code microsoft freeware ) to write C++ for the
++ USB connected Raspberry Pi PICO board with:
+++ RP2040 microcontroller
**** a Dual-core Arm Cortex M0+ processor,
**** flexible clock running up to 133 MHz,
**** 264KB of SRAM
and more elements on the board:
+++ 16Mb ( 2MB ) QFlash,
+++ 12MHz Crystal
+++ Buck-Boost DC-DC converter input voltage (~1.8 to 5.5V) to 3V3
+++ [bootsel] button
+++ user LED ( GP25 )
+++ USB port ( micro-USB cable to RPI4/PC needed )
+++ and 43 pin ( GP 0 ..22 + 26..28 ADC + 2 diag ports )
( pins no header pre-soldered / provided / dual castellated/through-hole pins )

add i use other Arduino boards to check old code..
!Warning do not connect 5V Arduino boards to 3V3 PICO board!


reset button missing issue
ADC test
serial menu reborn
RPI BLOG-web-server with PICO IO: PoorManScope 3
RPI BLOG-web-server with PICO IO: PID ... Processcontrol
PICO Output PWM by THREAD
PICO Over Clocking
PICO and VSC and multiple *.c files and compiler options


reset button missing issue


when use C++ .. and need to load a generated *.uf2 file to the PICO first must
* take out the USB cable
* connect again while pressing the [bootsel] button
* and get the USB device file-manager window
* where you can drop that *.uf2 file
* what causes a reboot of the PICO and ?install? that program as main

that is annoying and the hardware solution is to install a add [reset] button what needs to be pressed
together with the [bootsel] button...
already implemented on some new third party boards.

but now there seems to be a software sollution:
hackster.io and raspberrypi forums and GIT
i need to explore that here:

make that flash program:
cd /home/pi/projects/pico/
nano flash.sh and paste


#!/bin/bash
sudo stty -F /dev/ttyACM0 1200
echo waiting
while [ ! -d /media/pi/RPI-RP2 ]; do sleep 0.1; done
sleep 0.5
if [ "$*" = "" ]; then echo rebooting; sudo picotool reboot; exit; fi
echo copying
cp $1 /media/pi/RPI-RP2
echo done

save it with [ctrl][o][ctrl[x]

make it executable
chmod +x flash.sh

make a alias for it
nano /home/pi/.bash_aliases
add line
alias flash='/home/pi/projects/pico/flash.sh'
and need to close open terminal ( to activate new aliases )
update here a few new alias settings to give you a idea about my working environment:
alias PICO='minicom -b 115200 -o -D /dev/ttyACM0'
alias flash='/home/pi/projects/pico/flash.sh'

alias flashpico='/home/pi/projects/pico/flash.sh build/pico.uf2'
alias flashnuke='/home/pi/projects/pico/flash.sh /home/pi/projects/pico/PICOdownloads/flash_nuke.uf2'
alias flashpython='/home/pi/projects/pico/flash.sh /home/pi/projects/pico/PICOdownloads/rp2-pico-20210205-unstable-v1.14-8-g1f800cac3.uf2'

echo -e 'USE: flash flashpico flashnuke flashpython PICO'


now test it:
cd /home/pi/projects/pico/blink/build
flash blink.uf2
say:
waiting
as this was only step one, we need to unplug/plug USB cable to PICO
and then the flash == drop is performed automatically.
pi@RPI4:~/projects/pico/blink/build $ flash blink.uf2
waiting
copying
done
pi@RPI4:




note: i deleted my /pico/ directory ( 10.3.2021 )
and run the setup script again just to have updated SDK and EXAMPLES
and possibly there NOT NEED to do following changes again?!?


ok now he wants us to change some library??
first make a backup:
cp /home/pi/pico/pico-sdk/src/rp2_common/pico_stdio_usb/stdio_usb.c stdio_usb.c.org
now edit:
nano /home/pi/pico/pico-sdk/src/rp2_common/pico_stdio_usb/stdio_usb.c
insert ( after the other #include ):

#include "pico/bootrom.h" //HSW
#include "hardware/watchdog.h" //HSW

void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding) //HSW
{
if (p_line_coding->bit_rate == 1200) { reset_usb_boot(0, 0); }
if (p_line_coding->bit_rate == 2400) { watchdog_reboot(0, 0, 0); }
}

and test it:
cd /home/pi/projects/pico/blink
rm -r build
mkdir build
cd build
cmake ..
make blink
flash blink.uf2


ok here it worked AFTER i did the cable and [bootsel] button thing one more time manually

possibly wait what will come up about that in future,
but instead to build 2 instead 1 button on the board now we not need buttons at all
that is a big progress.


WARNING!
usually this is for projects where the USB is connected all time, and also you might use
printf(""); ( if even for diagnostic )
so you have the 'minicom' window open... that is usually no problem..
while you load the new code with 'flash X.uf2' command
the minicom terminal show that the port is unavailable and recoveres after pico auto reboot.

! but if you:
* stop the shown datastream in minicom by [ctrl][a]
* and then try a flash
you might end up in rebooting RPI and [bootsel] button operation while flash,
to get it all working again.



here a note about a project structure:
we started with making a
* new directory for our code ( with a descriptive name )
* now instead use a program code name blink.c i think better use always the same name
pico.c ( containing a main(){} )
* we need the link info to the pico sdk installation, so copy the file
cp /home/pi/pico/pico-sdk/external/pico_sdk_import.cmake .
and tell in CMakeLists.txt:
include(pico_sdk_import.cmake)
OR do not copy the file
and tell in CMakeLists.txt:
include(~/pico/pico-sdk/external/pico_sdk_import.cmake)

* and we need that
CMakeLists.txt ( what now only links to pico.c / and creates a build/pico.uf2 and not need to be changed too much )
* optional have a
README.md
where your project description is better ( as in the code file name )

now manually can do above build sequence or let it do the VSC, type:
code
in VSC select the 'project' directory
select the 'GCC for arm-non-eabi 7.3.1' compiler
press 'build'
in terminal ( inside VSC or external ) type:
flash build/pico.uf2

then can call up the minicom by our alias 'PICO'



HERE a good default code ( like for start a new project )
code



ADC test


i try first the analog inputs but got very confused,
SDK talk input 0 .. 3 GP26..29 but there is no GP29 ( pin ) it is used internally on the board.
and input 4 is the internal temperature sensor



serial menu reborn


one of my oldest projects is to try to make something like a
multi tasking operating menu for arduino
here i try now to find it and load it on a Arduino Leonardo by RPI4 Ardoino IDE
and then make a version / port / for the PICO
first setup work-environment:

looks like the latest version i found is 3.1

so what is the idea?
on a micro-controller you should never code using wait...
a basic loop should run as fast as possible
and using a counter and compare it with counter setpoint for each JOB
if you need the JOBs to be executed according a TIMER you check that
in that basic time-slice made by the counters,
do not check timers at every loop as the time code itself is time-consuming.

here the basic loop runs 1.000.000 times in 8 sec ( yes a operating system is time consuming ..)
( as also some JOBs are running and strings send out... )
my empty basic menu on the arduino UNO / Leonardo needs min 4 sec/1M loops
later with the PICO i see about 3 times 1.000.000 loops in ONE second! about 12 times faster!

same with the serial input operation, just check for presence of one character
( list of menu-options ) over the USB input line,
do not wait for input unless you have to ask the operater a question and expect a longer string
( a test for operator sending a CSV style line and interpreting it for control is provided )

show the optional menu only on request, here [space][enter]
( depend on the terminal program if [ENTER] is needed )

in this version have the prepared options: 0 1 a C d t T s i and [space]

** like on [i] the answer is
5000 mV ,1023,827,697,604,538,498 of 1023
the Arduino Leonardo with ATmega32U4 cpu has
12 analog inputs 10 bit ( 0 .. 1023)
( here the first 6 where A0 is jumpered on Vcc and the other floating )
and operates at 5V from USB or up to external 15V conditioned to 5V.

if you like try it on a old Arduino board menu_kll_v31.zip

this basic program structure is not only a USB terminal menu,
i used it also via apps ( processing / python / even webpage like with operating PoorManScope)
for ?remote? control.

it is a time slice multi tasking, very different from a interrupt request driven structure,
what can end up very bad if the JOBs are too BIG


in step 1 using the PICO i play with ADC first
and the main counter structure and the menu on single char press ( to minicom )
and I DID IT AGAIN: the never too old CHART LINE
( A0 '0' and A1 '1' visualized on a 100 char line as semigraphic 3v3 voltmeter output )
more will follow / ongoing work
pico.c




up to now only the 2 version ADC jobs ( print or semigraph )
now added a manual RTC setting by type in new date & time.
( still better to adjust it in source ( with some seconds in the future ) and upload it ( flash build/pico.uf2 )
at THAT time / but i needed to play with that number input code anyhow ( no echo / no check.. )

i did not copy any code from the original arduino project, just 're-learn' it.


RPI BLOG-web-server with PICO IO


now that is a bigger project and i am not sure its worth it,
but it has been some time i tested my BLOG web server,
so first try to get that running on the RPI4.
( it is not that i was not interested in web development,
i play a lot with NODE STRAPI or TOTAL.JS for learning )

but i hope the old python / werkzeug / Flask BLOG
( the myblog_094.tar.xz from this download section ) is still running.
( and the download counter shows 500 downloads of it! )

-1- unzip/copy to /home/pi/projects/myblog
-2- cd /home/pi/projects/myblog
-3- ./install.sh
say y to the questions for install firmata/mqtt/arduino options if needed,
and find 2 icons on desktop:
* myBlog server start and
* myBlog browser start
-4- double click server start and double click browser start
WOW!



[menu] Login
user: admin
pw: default ( please change later )

the login brings you to the BLOG work section, use MENU to open like PMS3


now i can try the add ons i did with arduino, but using the PICO!
first test them with here Arduino Leonardo again:
copy :
~/projects/myblog/tools/sketch_book/PMS34/
~/projects/myblog/tools/sketch_book/FLASK_PID/
to your Arduino code directory ( here i adjusted the arduino IDE to
~/projects/Arduino
as sketchbook directory )
start PMS34 Poor Man Scope / 3 Channel / Revision 4
and try upload to arduino
( very bad, have ERROR1,
need to delete 3 TABs:
/myblog/tools/sketchbook/PMS34/chart.ino
/myblog/tools/sketchbook/PMS34/common.ino
/myblog/tools/sketchbook/PMS34/sample.ino,
now upload ok )
? my IDE copy / rename error shortly before project zip
/ i did find now a PMS3 rev5.2 from 8/2019 on my backup drive, need to check later/?


now what is that about:

while for a print or line-chart you sample Ain and print a line....
this will give you a limited sample rate ( independent how fast the controller is )

for a scope i sample as fast as possible 320 samples ( 1 .. 3 channels ) to a array
( for arduino even use a different analog read code.. )
( for UNO and Leonardo only 2 channels enabled due to memory problems )
and print that array data in a CSV line style in a batch.
( please see that first line of that batch print what give info how to understand that data )

info:
the 320 samples batch length comes from the memory limit of the Arduino UNO ATmega328P
( for the array 3 ( 2 or 1 ) channel per sample line ) and gives a scope window of 320 dots / pixel

via a usb serial menu can give commands like
* how many channels you want sample
* and at what sample rate.

and use
* the terminal ( to see the print )
* a application like processing or python
* or like here a python service to provide that data for the python flask web server.

now lets start that service

the install already setup that commands for you, use:
./start_pms3_service
that will run pms3_arduino_stream.py

pi@RPI4:~/projects/myblog $ ./start_pms3_service
start python arduino service, stop with [ctrl][c]
Found ports:
/dev/ttyS0
/dev/ttyACM0
port: /dev/ttyACM0
very first summary line
catch: 361 lines, in 1.97s, file: /run/shm/PMS3_1.csv head: ,5,channela,0,channelb,1,channelc,_,batchtime:msec,268,samples,360,samplefreq,1342,Hz,
678,1023,0,
679,1023,0,
678,1023,0,
...

( i did jumper the A0 to 3V3 and the A1 to 5V here on the Arduino leonardo)

this collects the streamed data ( 320 lines of 3 channel samples ) to CSV file
in RAM DISK of RPI OS ( /run/shm/ ) so not wear out the uSD card.

and now start the web server again ( icon on desktop )
and, after login,
via the web server BLOG menu PMS3 can use the rpi_pms3.py
interface to show the SCOPE
ERROR2: i not see the chartline??
but the operation [slow][fast] sampling [+][-] is indicated correctly in the header line
( what comes from the GOOD datastream )

so the 2018 revision what worked here and here must be packed wrong OR i have a python flask version problem.
i do not have the RPI3B+ system SD anymore where i play with that, only a backup from 9.7.2015

hm.. i try in 'rpi_pms3.py' with diag3=True
and the data read from RAM DISK look good
also check in browser for developer tools: console no errors,
so who make that page?
/myblog/templates/PMS3.html ( from 3.6.2018 )
( when i edit that file ( on RPI ) i get a warning that it was converted from DOS format???
did i transfer it to windows PC )

it uses SVG
to generate the 3 scope windows ( one for each channel )
and in there it makes a window rectangle, adds the DIV lines
and then 360 lines all start x1 6 to x2 6 ( yes that is what i see, but why? )
i check from different computer and browser, and browser settings... and SVG restrictions??
but i get the idea that it is a counter problem on the
"FLASK Werkzeug Jinja2" side
what should create the 360 lines ( and it does it, just all on the same position )
a simple {% set count = count + 1 %} does not work in that loop over all samples.

Flask 1.0.2 use Python 3.7.3 (default, Jul 25 2020, 13:03:44) [GCC 8.3.0]
Jinja2 Version 2.1
Werkzeug 2.1 Version 0.14.1

i did not install or update here anything, its like the RPI dist-upgrade has it as default
ok, i changed that external ( and now not working counter ) with
jinja
see: loop.index0
i use now as {{ 6+ loop.index0*2 }} as position for the lines of the graph.
WOW, that was a bad one
changed:
myblog/template/PMS3.html
myblog/template/help.html


now i also for test change the hardware, connect a UNO for sampling ( instead the Leonardo )
++ and use a DUE what has a DAC, Aout to generate a signal ( for a better show here )




good, now i can start work to make that program new on the PICO

the batch loop:
360 * 3 * ( adc_select_input(x); adc_read(); )
settings dtp 1 read 3 channel needs 4 msec ( 90kHz sampling rate ),
for settings dtp 0 read channel A0 only about 1 ms ( 200kHz sampling rate )
( pico start with dtp 5 what means 40ms batchtime )
first pictures, no good signal, not checked...

looks already good.
here the pico.c code


PICO Output PWM by THREAD


long not finished with the PICO PoorManScope 3 ( web server view )
why i start with something new?
interludium
to check the PMS3 i need a signal.. and most ( besides the Arduino DUE ) boards have no analog output
also i can not expect that you have other Arduino board available...
what comes close to a analog output is a pulsing digital output...
( they use it to drive VGA screens ... by 5 channel to different resistors ( times 3 for RGB ) )
PWM means Puls Width Modulation, its like a square-wave signal where the high-side can be adjusted in length.
RPI forum
i want that kind of signal available on the PICO for check the SCOPE, but without
slowing the PMS array data collection down.
so that i want PWM fade.. to run in the second cpu of the PICO called THREAD
-a- start with the onboard LED PWM example
-b- measure its timing
-c- make THREAD
-d- move the PWM code to THREAD
-e- measure new timing
-f- change from LED to pin output for jumper to Ain0
code

now the timing tells me:
the PWM alone not takes time in main but still i start it from THREAD init
the rampup takes time (120ms of 350ms ) and better is in THREAD

all the initial prints i can never see??? even i have the minicom open while flashing



ok now we copy the
* PWM_in_THREAD code into the
* PMS3 for PICO

add i want to adjust the signal:
in PICO 4096 is 3v3
in SCOPE 1023 is 5V as we show 5 DIV lines ( mapped to 250 px graph height )
so change the print to USB.
// int thisA0 = allA0[count]>>2; //_________ 4096 to 1023
int thisA0 = (int)(allA0[count]*0.165); //__________ 5v down to 3v3 and /4

for a resolution of 250px in the graph that is ok, but yes, give up that good PICO
12bit ADC data ( Arduino 10bit ) is usually a bad idea. but i do not have / or plan /
a SCOPE signal zoom / move function... ( it is a Poor Man Scope only)

PMS3 & PWM


now the PICO has a update rate to USB what depends on the sample rate you asked for [+][-]
in the ./start_pms3_service terminal you see the actual array records to RAM DISK ( diagnostic print )
the PMS3 browser window (see /myblog/templates/header.html ( if autoupdate use 10 sec )
has a comparable slow auto update rate, you can,
like after operate [+][-],
or on changing signals like our PWM ramp
force a update with the browsers "reload current page" button.

now as we see a PWN signal ( changing ) but that is not:
- - a verification of the ADC ( would need a good saw wave signal generator and a calibrated voltage source )
- - a verification of that frequency info ( calculated by PICO delta micros of 360 samples batch )
also the math is little bit tricky:
as the scope has 360 samples in the batch/ scope-window,
and in case you would see ONE SINUS in that window,
! lets just say of a 1 Hz signal
the sample rate would then be 360 Hz. ( and sure a batch time be 1sec )
so here, i just copy the PWM code, no idea what is the base frequency i used???
( default with divider 4 ???)
now we see 3.5 PWMs in 360 samples, measured in 7.1ms (50kHz sample rate? correct ? )

see forum make 440Hz 'A' example
possibly better create two PWM signals to better cover the scope range, and how to verify?
use that supposed 440Hz

shows:

i see 10 waves in 22.4ms ?calc 1/0.00224 ? 446Hz ? could be
ok, but i am not happy with the PWM and RAMP ( and THREAD),
think will delete all from PMS3 but a easy PWM 1kHz base 10% ON on pin 29 GP22
checked delay-timer / batch-time / sample-frequency:

int dtp = 5; //__________________________________ start pointer setting ( 0 means only sample A0 no wait / 1 means sample A0&1&2 no wait )
const int dtpm = 13; //__________________________ max selectable number by browser operation ( already see com errors "too short line" )
int batch_tune [] = { 0, 0 , 10, 25, 50, 100, 500, 1000, 2500, 5000,10000,25000,50000,100000}; //_ sleep us after sample
//__ dtp ____________ 0___1____2___3___4____5____6_____7_____8_____9____10____11____12_____13______ pointer by [-][+] ( 10 about 100Hz )
//__ ms_______________2___4___ 7__13__22___40__183___364___900__1805__3603__9000_18000__36000______ batch time
//__ Hz____________200k_90k__50k_28k_16k___9k___2k___990___398___199___100____40____20_____10______ sample freq


this code

also there is a idea to sample one channel only 8bit but with 500kHz find in the examples


also i not used it now in above code, i still go and set up a other project
better sinus in thread
because i did here something on arduino


RPI BLOG-web-server with PICO IO: PID ... Processcontrol


if you want to use the PICO board not only for Ain, also for Process Control System PCS
_ it must run:
* * analog ( PID ) and
* * analog indicators with alarming
* * digital control (PLC style )
* * digital indicators with alarming
* * * also have trending / tuning ...
find in myblog menu:

uPCS


first we try to load a Arduino with this control code "Flask-PID"



for my friends from the "old times":
i worked ZIMMER AG Frankfurt ( founded by the papa of Hans Zimmer / who makes that fantastic movie music for SCIFI/Action movies )

you remember when we build ProcessControlSystems, controlling chemical factories around the world
for half-million-$, covering 2 rooms ( control room and I/O room )

like from 'EMERSON Fisher Rosemount DCS DELTA V' ... ( but also from Siemens, Yokogawa... )
or the very first ones 'PROVOX' still using VAX computers from 'digital'
( i think in my electronic garbage i still have a development dongle what costs today 30.000$ )
( see also MY RETRO PI here )

well just understand: here today my
* * Engineering station and Diplay Control Unit DCU costs 55$ (RPI 4 w. 4GB RAM)
( plus a TV screen & keyboard & mouse ) and my
* * a Process Control Unit PCU costs 4$ ( Raspberry Pi PICO even only with 3 Ain.. 23 digital I/O )



now start myblog USB server and webserver and see if we have same bad luck as with the PMS3
./start_upcs2_service

also try again with Arduino UNO ( this Leonardo serial is very different )
and see that lots of RAM DISK files are created

also when i did the PMS i go for serial lines CSV style very readable,
but without header line NOT understandable!

when i started uPCS i try JSON what has 3 times more text, but should be error free,
but actually i not find it that readable.
ok, load it to a Arduino UNO, see same!

when i test it in Arduino monitor and give it a manual JSON command
i get one round of good data back and stops ( by internal timeout when miss requests from master ).

now start the web server
./start
( or from dektop icon )

*** you must be login as 'admin' 'default' to OPERATE!

so the operation is very slow,
-a- change a value / like use the slider for setpoint SP% or output OV% for:
* * SP in AUTO or
* * MODE [press option button] or OUTPUT in Manual

-b- press the [change] button ( means SEND )
if you wait too long with the button your input is cleared by the page refresh!

-c- anyhow the values jump back to the old (actual ones in PCU )
because of auto-update ( 10sec ) browser page, like they where not acknowledged, but wait...

-d- when a update from controller comes you get the right response.
( now i hope with the PICO i can improve the timing, not only control, also operation...


in the "current trend" you see that changes and actions very well, even no real field loop is connected / so PV not change ( jumper to 3v3 )

the bottom button bar show you all prepared displays.
* * Over View a process picture with dynamic 'mini faceplates'
* * Faceplate Group all points ( click on one for detail or operation )
* * Faceplate Detail selected point ( here the PID control loop / in login / operation )
* * Current Trend
* * Historic Trend ( that should be saved but sorry, still only in RAM DISK, copy manually for now)
* * Log
* * Snap


ok, lets do same with the PICO ( even use same device name "UNO1" / sorry @EbenUpton )
( starting from below shown "multi c files" example code. )

a PCS thinks in Control Points "loops":
+ + analog indicator ( filtered analog in )
+ + analog control ( PID )
+ + discrete control devices ( digital input and output logic point ( like a PLC ) )
- - Di or Do points ( no example here )
this code shows only
+ IND1 .. 4
+ PID1
+ DCD1
as PICO has reduced analog capability:

i copy that from a good youtube video, sadly when he say "channel 1" is actually called by "adc_select_input(0);"
so i find the 1 ..5 confusing, better use 0 .. 4
+ PID1 input 0
+ IND1 input 1
+ IND2 input 2
+ IND3 show the internal temperatur sensor 4
+ IND4 uses same input as PID1 ( hope to connect a I2C sensor later )
+ PID1 output use GP22 as PWM output as PICO has no DAC

the 5 analog loops are running by a scheduler:
PID1,IND1,PID1,IND2,PID1,IND3,PID1,IND4 with dt 0.5sec ( later test faster ... )
( that timing can not be free, as there are signal filter on the inputs what makes that tricky, means must tune all filters again )

this time i hope to reuse most of the control code from my Arduino example,
what actually means i have to write anew the Arduino IO language functions like:
analogRead, analogWrite, digitalRead, digitalWrite,

other big problem is the printing...
i take out all compiler macros and replace with 'printf' use%.. and manual linefeed \n ...

now how PICO reacts?
the 1Mloop change from 290 to 400 for the scheduler ( without jobs ) to 1500 [milliseconds] for all control code
incl printing lines ( every 0.5 sec one point ), good for me, sorry not remember that value for Ardunio boards.
wait, the serial code ... operation.. not yet coded.
the Arduino IDE needed some year until the C String lib was included, and my old code
uses it ( anyhow missing a JSON library, its ugly coding )
but here we only have
#include "string.h" & "strings.h"
what give some char array functionality NOT String
also it is not to find in the SDK, pls see:
/usr/include/newlib/string.h
more help here
and with that i will try to take the JSON command-strings/lines from the uPCS python service apart replacing original "String" code,
but not happy about that way ( without a JSON lib i should have stick with CSV lines style )


IN_WORK


PICO Over Clocking


try about this ( see links to info in code )
not all worked, also when failed the serial port not worked, then the flash trick also failed...
need to do the cable remove [boosel] button way to upload again.
code



PICO and VSC and multiple *.c files and compiler options


there is a example called 'cmake' what actually is a very good learning resource
because it is about compiler variables, options, and make projects from several *.c files
( could call it your own project library.. just with the difference that its in your project and not external
and you can write / read in IDE all that files. actually like Arduino IDE has that TABs ( multiple *.ino files )
what is easy to use as it hides all what you have to do here now.
i find it tricky to get it running and sure failed several times about variable scope..
anyhow i build in my serial operation menu ( minimal version ) in a extra menu.c file again to give you a ? starting framework ?
code ZIP



now it looks like i have a chaotik working style, but i gom back to PC and
* Arduino IDE 2.0 test,
* install the ESP32 board tools
* search for the MQTT sources i made ( while working uPCS2 )
pls find that excurs here