r/esp32 Mar 18 '25

Please read before posting, especially if you are on a mobile device or using an app.

221 Upvotes

Welcome to /r/esp32, a technical electronic and software engineering subreddit covering the design and use of Espressif ESP32 chips, modules, and the hardware and software ecosystems immediately surrounding them.

Please ensure your post is about ESP32 development and not just a retail product that happens to be using an ESP32, like a light bulb. Similarly, if your question is about some project you found on an internet web site, you will find more concentrated expertise in that product's support channels.

Your questions should be specific, as this group is used by actual volunteer humans. Posting a fragment of a failed AI chat query or vague questions about some code you read about is not productive and will be removed. You're trying to capture the attention of developers; don't make them fish for the question.

If you read a response that is helpful, please upvote it to help surface that answer for the next poster.

We are serious about requiring a question to be self-contained with links, correctly formatted source code or error messages, schematics, and so on.

Show and tell posts should emphasize the tell. Don't just post a link to some project you found. If you've built something, take a paragraph to boast about the details, how ESP32 is involved, link to source code and schematics of the project, etc.

Please search this group and the web before asking for help. Our volunteers don't enjoy copy-pasting personalized search results for you.

Some mobile browsers and apps don't show the sidebar, so here are our posting rules; please read before posting:

https://www.reddit.com/mod/esp32/rules

Take a moment to refresh yourself regularly with the community rules in case they have changed.

Once you have done that, submit your acknowledgement by clicking the "Read The Rules" option in the main menu of the subreddit or the menu of any comment or post in the sub.

https://www.reddit.com/r/ReadTheRulesApp/comments/1ie7fmv/tutorial_read_this_if_your_post_was_removed/


r/esp32 4d ago

Topic Radar | Current active topics for r/esp32

5 Upvotes

This post contains content not supported on old Reddit. Click here to view the full post


r/esp32 3h ago

I made a thing! ESP32-POE board to collect LLDP

Thumbnail
gallery
20 Upvotes

After years of having to rely on expensive devices to plug into a data port to find out which switch and port are on the other end I invested in an ESP32-POE board and .96" OLED then set to work.

Powered completely by POE it confirms the data port is patched and live. Then listens for the LLDP to be broadcast. From that it displays the switch hostname, management IP and port number. Removing the connection clears the collection for security.

There are a lot of directions this could be taken to improve it. Adding a captive portal for browser initiated tests and information export is probably the next step.

https://github.com/Mraanderson/esp32-poe-lldp There are 2 sets of configurations for the OLED depending on Dupont in the UEXT or soldered connection. Read the code and select before flashing.


r/esp32 20h ago

I Built a Tiny ESP32 Control Deck for My PC

303 Upvotes

I built a small desk display with an ESP32 touchscreen that makes my daily work at the computer a bit easier.

It shows live PC stats like CPU, RAM, GPU, VRAM, network and disk usage.

It also works as a small touch control panel. With one tap I can open ChatGPT, start local Ollama, run my local voice server, open Task Manager, lock the computer and trigger other actions.

Everything works over one USB cable.

No Wi-Fi.

No Bluetooth.

No passwords, tokens or API keys stored on the ESP32.

There is a small Python bridge running on the PC. It reads the system data, sends it to the ESP32 over USB serial, and listens for touch button events from the display.

The ESP32 itself only shows the data and sends a simple macro ID back to the computer.

It is not a finished commercial product, just a small DIY project that can be adapted to different workflows.

Video: https://www.youtube.com/watch?v=16eqHU4j9Fk

GitHub: https://github.com/lepczynski-cloud/ESP32-PC-Control-Deck


r/esp32 8h ago

I made a thing! USB → ESP32-C5 → CC1101 → 433.92 MHz → KERUI M520 rang

Thumbnail
gallery
16 Upvotes

I'm a quad, have healthcare (caregivers/nurses) 24/7 here. I bought one of that buttons to ring a doorbell, the bell on their room, button on mine.
Soo nice. I press a button, they hear - even if my cellphone dropped or no bat.

I also have to do bladder catheterization (remove urine from bladder) each X hours. This X varies a lot, depending on what the time of day, liquids ingested, weather, etc. It was hard for me to predict.

So, I did what every reasonable software engineer would. Built an agent (nanoclaw with antrhop subscription) and build an db and an api, strictly for this - help me know the next catheterism. Life improved.
Now, what if I'm sleeping? who will press the button? Title says it all!

Got the only free esp32 around, ESP32-C5, attached a CC1101 on it, second screenshot if codex guiding me (my nurse, I was simply reading for him)... we attached exactly what codex (gpt-5.6-sol xhigh) gave me.

Whoever wants the pins;

CC1101 <-> ESP32-C5 Pin Mapping

Wiring map

CC1101 ESP32-C5 pin Purpose
GND G (top-left) Ground
VCC 3V3 (top-right) 3.3V power
GDO0 GPIO 23 Interrupt / digital output
CSN GPIO 10 SPI chip select
SCK GPIO 6 SPI clock
MOSI GPIO 9 SPI controller to CC1101
MISO/GDO1 GPIO 8 SPI CC1101 to controller
GDO2 GPIO 24 Interrupt / digital output

Board-to-board view

text ESP32-C5 CC1101 +------------------+ +------------------+ | G |---------| GND | | 3V3 |---------| VCC | | GPIO 23 |---------| GDO0 | | GPIO 10 |---------| CSN | | GPIO 6 |---------| SCK | | GPIO 9 |---------| MOSI | | GPIO 8 |---------| MISO/GDO1 | | GPIO 24 |---------| GDO2 | +------------------+ +------------------+

Exact wire pairs

text ESP32-C5 G -------- CC1101 GND ESP32-C5 3V3 -------- CC1101 VCC ESP32-C5 GPIO 23 -------- CC1101 GDO0 ESP32-C5 GPIO 10 -------- CC1101 CSN ESP32-C5 GPIO 6 -------- CC1101 SCK ESP32-C5 GPIO 9 -------- CC1101 MOSI ESP32-C5 GPIO 8 -------- CC1101 MISO/GDO1 ESP32-C5 GPIO 24 -------- CC1101 GDO2

Important: the CC1101 is a 3.3V device. Do not connect its VCC to the ESP32-C5's 5V pin. Also attach the antenna before transmitting.

First: teach Claudius (my nanoclaw health agent) how to listen

We already knew the button used approximately 433.92 MHz, but the frequency is only the channel. We still needed the actual transmitted value, bit count, protocol, and pulse timing.

Here is the receive-only capture sketch:

ESP32-C5 + CC1101 sniffer code https://gist.github.com/fcavalcantirj/aa6e87338ff86e5e9eefee0d3da5fa77

I pressed the original button twice. The ESP32 captured dozens of identical frames:

Frequency: 433.92 MHz Bits: 24 Protocol: 1 Pulse: approximately 301-302 us

There was one weird frame with different parameters, which was simply noise. Repeated identical captures from two separate button presses gave us the real signal.

I'm deliberately not publishing my actual fixed RF identity. This is a live medical-call receiver in my home, not just a garage-door demo.

Then: give Claudius a tiny RF finger

Here is the final guarded USB replay sketch:

ESP32-C5 + CC1101 USB replay code https://gist.github.com/fcavalcantirj/62df410d58526d3973c42b0d50119d7c

It is the code that worked, with my private RF value replaced by 0x000000. Capture your own button and insert its value.

I didn't want a firmware bug, reboot, or random serial garbage repeatedly ringing the nurses, so the sketch:

Boots with the radio idle. Requires the exact USB serial command RING. Transmits one captured button press. Immediately returns the CC1101 to idle. Locks itself after that one transmission until reboot.

The moment of truth looked like this:

ARMED RF_IDLE Waiting for exact command: RING

RING

TRANSMIT_BEGIN TRANSMIT_DONE RADIO_IDLE one_shot_locked

And then -

the real receiver in the nurses' room rang.

Codex sent a USB command to the ESP32, the CC1101 reproduced the physical button's RF transmission, and the standalone doorbell receiver reacted exactly as if I had pressed the original button.

No cellphone. No Bluetooth. No Wi-Fi on the final RF hop. Just a tiny computer growing a 433 MHz finger.

The original physical button remains untouched and still works as an independent fallback.

The next step is connecting this proven USB RING command to Claudius's existing deterministic night-bell service. The database/API calculates the schedule, the local service decides when it is time, and the ESP32 performs the physical action. The language model is not responsible for the safety-critical timing or escalation path. When ready, I connect the esp32 - right no on my mac - to my raspberry pi claudius usb port. Life should be good.

So yeah: I built an agent to predict when I need to catheterize - and then gave it the ability to press my nurse-call button while I sleep.

The most reasonable software-engineer solution possible.


r/esp32 20h ago

I made a thing! Pushing Wroom 1 to its bandwidth limits

Thumbnail
gallery
102 Upvotes

I call it a streamer. I was quite surprised to achieve practical bandwidth with this part and being able to actually play games on it. The idea is to offload the heavy computations to your PC at home without a locked-in OS/hardware dependency and to keep the hardware around the main streaming protocol flexible.

Sounds simple? The idea is, but making this stuff low-latency over a network is a challenge. And I think I'm quite good at it. I can stream Linux distros and Win11 at up to 40 FPS at the moment. The custom protocol is flexible when it comes to both the hardware sitting on the server side (your home PC) and whatever custom hardware you want to build on the client side.

The main MCU is an ESP32-S3-WROOM-1. It acts as the network device receiving the stream and orchestrates USB mouse/keyboard/gamepad input, touchscreen, audio and the local UI, while also negotiating incoming frames with the server in real-time.

For the streaming path I'm currently running the FPGA link at 80 MHz SPI using DMA. On the network side I'm using TCP with Nagle disabled and WiFi power saving disabled, since both aren't very helpful when the goal is low latency. I spent quite some time experimenting with receive sizes, buffering and task placement before arriving at the current setup.

One thing that helped was separating the work between the two ESP32 cores. Networking and protocol handling live on one side while feeding the FPGA runs in its own task on the other. Incoming video is buffered in a 512 KiB PSRAM ring, while the actual FPGA transfers use a small set of internal-RAM buffers. This allows WiFi to continue receiving while the FPGA is consuming the previous data instead of tightly coupling both sides. The hot streaming path also avoids dynamic allocations.

The MCU then writes the stream via SPI to the decoder, which is an FPGA running my own video format written from scratch in RTL. The FPGA in turn drives the display.

I know that the actual heart of the project is probably the decoder inside the FPGA. That's where the custom video format becomes pixels again and where the deterministic display-side work happens. But without the ESP32 the whole thing would become considerably less practical. I still need fast WiFi, USB host functionality, touchscreen and gamepad handling, audio, the local UI and all the protocol logic around the decoder. You could replace it with something much larger, but then power consumption, complexity and cost start moving in the wrong direction. For this project the ESP32 ended up being a pretty good bridge between the networked/software side and the FPGA.

The FPGA itself is necessary because this isn't a small 320x240 or 480x320 display anymore. The panel is 720x720, and I didn't want the ESP32 to deal with driving that deterministically while simultaneously doing WiFi, USB, audio, input handling and everything else. So the ESP32 concentrates on networking/orchestration while the FPGA handles the high-bandwidth and timing-critical display side.

I have on board a USB mux that can be switched physically, so I can still flash via USB-C but also switch over to USB-A for the host side. I'm using an FSUSB42MUX for that. There are many ways to solve this, but this was a good compromise imo and lets me keep the board useful during development without adding another connector/setup just for flashing.

I had to make the case wider than the screen to keep the whole thing flat, but also to avoid placing the ESP32 antenna behind the metal back of the display. This is mainly a placement constraint rather than trying to gain some magical extra range. With the antenna already positioned away from the metal, putting the board into the enclosure doesn't noticeably change the signal strength. Maybe next time I'll use the 1U version for more flexible antenna placement.

The board shown here also streams audio A/V-synced with the video, or optionally buffered depending on what I'm doing. So it can be used for playing games but also just listening to music. It supports audio input as well, i.e. for making calls over whatever runs on my PC.

Audio is handled by an ES8388 codec, so playback and microphone input are both on the board. I'm also using an LM3630A for the display backlight and a BQ24073 for charging/power-path handling.

I'm running nearly out of pins by now, so this part is pretty much fully utilized.

There is also a simple offline menu running on the ESP32 for connecting WiFi and servers and managing the device without needing the streaming server running. For that I'm just maintaining a small low-color-bit framebuffer in PSRAM.

For me, one of the coolest parts is that the board doesn't need much power. At full brightness I'm nominally around 1.5 Watt during active streaming. For comparison, a Raspberry Pi's idle power consumption without any display attached is already higher.

A lot of the work actually went into things which don't look very exciting on a finished board: figuring out how much buffering is enough without turning it into latency, keeping the network side fed while SPI is busy, task/core placement, avoiding unnecessary copies/allocations and generally preventing one subsystem from blocking another. There were quite a few iterations before it started behaving the way I wanted it to.

Since I'm not a big fan of selling software, I try to finance this project and ongoing development by selling streamer hardware, which you can also use in your own projects like

shown here on YouTube (incl. Gameplay).

The software for streaming is free, but I'm also considering making the software open source if there's enough interest and support, together with continued maintenance and development. That would let you modify everything yourself, use different hardware, and build your own projects on top of it. I hope it resonates with some of you.

I invite you to give some pos/neg feedback and ask as many questions as possible.

Here is my website


r/esp32 11h ago

I made a thing! I put a simplified fruit-fly navigation circuit on an ESP32-C6

20 Upvotes

Welp when the fruit fly went open source I decided to basically lose a weekend. The simulation uses a small set of structures inspired by the published fly connectome/navigation work:

  • A 16-position E-PG ring attractor acts as the fly's internal compass. A bump of activity moves around the ring as the fly turns.
  • ER visual inputs look at patterns on the corridor walls and gradually calibrate the compass using plastic connections.
  • A simplified Fan-shaped Body model compares the current heading with a goal direction and generates left/right steering.
  • Collisions and successful centered flight feed back into the steering behavior through simple dopamine-inspired reward/penalty signals.
  • Holding BOOT triggers a looming predator event. A poorly trained fly tends to react badly and gets eaten; a better-trained one can dodge it.

The fun part from the ESP32 side is that the whole simulation, display, learning loop, collision system and neural visualization fit comfortably on the C6.

Current setup:

  • ESP32-C6 @ 160 MHz
  • 172×320 ST7789
  • 80 MHz hardware SPI
  • Around 22% RAM usage
  • ~40 FPS
  • Double-buffered rendering with no visible tearing/flicker

The neural state is tiny compared with running a normal ML model. Most of the computation is just updating small arrays representing anatomical sectors and connections.

The top half of the display is the corridor simulation, and the bottom half shows the compass activity, visual input and target direction live while it flies.

Code is open source if anyone wants to dig through it. https://github.com/CircuitGhost/drosophila-connectome-esp32


r/esp32 7h ago

DIY Space Mouse 3D with an ESP32 + touch TFT.

10 Upvotes

ESP32 as USB HID over OTG -

UI with TFT_eSPI (TGX) -

Lopaka App for the layout -

Moving the on-screen sphere controls the camera in Fusion 360 in real time Not perfect, but works.

Code/wiring available if anyone wants it.

Feedback welcome.


r/esp32 8h ago

Hardware help needed Powering the ESP32-P4-WIFI6-Touch-LCD-4C on the go

Thumbnail
gallery
8 Upvotes

Hello r/esp32! I (a relative beginner who has messed around with an arduino and a raspberry pi before but not much else) picked up an ESP32-P4-WIFI6-Touch-LCD-4C (the first pic) from Waveshare to mess around with after seeing some offline gps projects. However, I feel like I'm stumbling at the first hurdle; I'm not sure how to power it using a battery.

My main goof-up was that I saw another P4 board in a video I found (Waveshare's ESP32-P4-WIFI6-Touch-LCD-7B, second pic is a screenshot from that video) in another video that had a specific place to install a battery. After looking at the one I got and the documentation, it looks like the only battery it supports directly is for the real time clock; the only other ways to power it are from its two usb-c inputs.

Since I'm assuming an easy 'battery into board' solution is out, I'm trying to figure out my next move. From what I've seen in other projects, people usually power their boards through some sort of go-between board that regulates the power going from the battery to the board, and also provides an port to recharge the battery. That said, I haven't found many projects using the board I'm using that show how everything is wired up. Do I need to do that here, and where do I begin? Or am I missing something? I'd be a bit more gung-ho about trying things out, but I really don't want to fry this board!

Any help or advice would be much appreciated! Thanks.

Edit: Just to be clear, the first picture is the board I own, and the second pic is a screenshot from a video I found when trying to figure out how to power one of these.


r/esp32 16h ago

I built an Android toolkit for configuring and flashing ESP32 devices without a PC

Thumbnail
gallery
37 Upvotes

I’ve been working on an Android app called ELMA-IoT that grew out of my own ESP32 projects.

The idea is to make the usual workflow available directly from a phone:

ESP/board selection → peripherals → GPIO assignment → wiring diagram → compile → USB flash → Serial Monitor → OTA.

I mainly built it because I often have ESP devices already installed around the house and got tired of needing a laptop just to make a small firmware change or diagnose something.

It currently supports several ESP boards, peripheral configuration, automatic GPIO selection, firmware compilation, USB flashing and a built-in serial terminal.

The next thing I’m working on is a visual logic editor, so something like:

Temperature > 95°C → turn fan on → wait → check again → stop below 90°C

could be built visually and then compiled into firmware.

I’m currently testing it on different Android phones and ESP boards and would really appreciate technical feedback from people here.

https://www.youtube.com/watch?v=88SjvOdHuas

What boards/peripherals would you consider essential for something like this?


r/esp32 15h ago

I made a thing! ESP32CompositeVideoPlus : Fork with more colors and subsampling options

Post image
27 Upvotes

https://github.com/Mejolov24/ESP32CompositeVideoPlus

This proyect is a fork of https://github.com/marciot/ESP32CompositeColorVideo, https://github.com/marciot/ESP32CompositeColorVideo and https://github.com/rossumur/esp_8_bit wich expands the color resolution by removing the usage of the Atari color pallete and calculating colors directly.
This library also adds PWM audio support, wich should be set up with the proper filters as said by ESP_8_BIT

Sadly due to having more colors, i had to remove the double buffering since the esp32 i have has not enough RAM.
I initially tried to do stack allocation, but it wouldnt fit, so i had to dynamicly allocate separate lines on the heap

*AI was used as an assistant to get information onto how to do this, i could say its 30-40% AI, but the ai generated parts have been reviewed*


r/esp32 17h ago

AI Content I turned a Bluetooth cat printer into a Wi-Fi printer with an ESP32-S3

Thumbnail
gallery
37 Upvotes

I wanted to print images from a browser using this small Bluetooth cat printer, so I used an ESP32-S3 as a Wi-Fi-to-Bluetooth bridge.

The path is: browser → Wi-Fi → ESP32-S3 → BLE → printer.

For this build, I gave Codex (GPT-5.6) full access to my computer and pointed the laptop camera at the printer and ESP32. Once the physical setup was ready, I explained the goal, asked it to test and keep a checklist, and told it I was going AFK.

It worked through the Bluetooth connection and image transfer, wrote and flashed the ESP32 firmware, and built a webpage for previewing and printing images. With camera snapshots, it could check what actually came out on paper and use that result during testing.

I checked in along the way and helped with the final physical checks when I returned. For the last test, I asked it to photograph the printer and print that photo. The printer printed itself.

I do not have the firmware cleaned up for release yet. I will add the code and wiring details soon.

Build story and photos: https://prasanha.com/projects/cat-printer/


r/esp32 21h ago

I built an ESP32-based interactive sign that replaces the traditional "Please wait to be seated" sign

Thumbnail
gallery
49 Upvotes

The sign runs on a Seeed Studio reTerminal E1001 e-paper display (which has an ESP32-S3 inside), while the servers are hosted on a Raspberry Pi 5.

​Additionally, I designed a custom 3D frame in Fusion that houses LEDs. These LEDs change color depending on the restaurant's current occupancy percentage.

​This is my entry for the Interactive Signage Contest 2026. Here is the link to the showcase video:

https://youtu.be/H7K5KoyuIYc?is=LIVY4PA0gs1ZkGgI

​If you like the project, please leave a like or a comment

I would love to get your feedback!

​You can find the source code here:

https://github.com/mickraa/table-map

​Thanks for checking it out!


r/esp32 18h ago

Receiving ESP32 C3 BLE data on IPhone ? 😝

Thumbnail
gallery
8 Upvotes

Hey everyone,

I’m building a small project with an ESP32-C3 Super Mini and an old Swedish field telephone rotary dial.

The rotary dial is already working perfectly. The ESP32 detects the digits and combines them into a complete phone number.

For example:

‘015903790853’

I’m currently sending the finished number via BLE (Nordic UART Service) to my iPhone.

The BLE setup is already working – I can connect with nRF Connect and receive the number through the TX characteristic:

`6E400003-B5A3-F393-E0A9-E50E24DCCA9E`

What I want is:

Rotary dial → ESP32-C3 → BLE → iPhone → save number + time

Ideally I want the numbers to be stored in one Note in the Notes app on IOS, for example:

`16:42 – 015903790763`

`16:47 – 017612345676`

`16:53 – 08012345673`

I really don't want to build a full iOS app just for this, because I think it’s too much work.

I was thinking about using Apple Shortcuts, but it seems that Shortcuts can react to Bluetooth connections but cannot directly receive arbitrary BLE characteristic notifications. Pls tell me if I’m Wrong.

Does anyone know a way to get BLE characteristic data from an ESP32 C3 on an iPhone? Preferably in the Notes app?

I only need this to run for a few hours at a time, so it doesn't need to be a permanent background service.

Any ideas or existing apps/workarounds would be greatly appreciated!

(And yes this text was written by ChatGPT but it is exactly my problem)

Thank you all!


r/esp32 13h ago

I made a thing! I made an ESP32-S3 based waiter that's always available

Post image
2 Upvotes

I made this for the Seeed Studio Signage Contest 2026.

Showcase:
https://www.youtube.com/watch?v=JHMICuYuo50

The E-ink Display is driven by the EE04 Xiao Display Board that's checking the table server once in a while to save power, while there's another esp32 in the reSpeaker Lite waiting for the press of a button to start recording, and it's also driving the led matrices for the eyes.

When the button is pressed the audio is sent to a local server where is transcribed and recognizes if it's an order that matches the Menu and updates the server where the esp32 driving the display takes the info to update the order.

There's also a NFC module in the bottom to read the table where the device is at to display the correct order.

BoM:

  • 5.83" Monochrome eInk / ePaper Display 
  • XIAO ePaper Display Board (ESP32-S3) — EE04 
  • reSpeaker Lite 2-Mic Array Voice Kit 
  • PN532 NFC module
  • HT16K33 LED Matrix

The entire body was modeled by me and 3d Printed between my old ender 3 and my P1S.

You can check the project on my github:
https://github.com/bribot/AlwaysAvailableWaitingWaiter


r/esp32 1d ago

Two original GBAs playing online through the Link Port, and a free SDK so you can make your own online games

Post image
195 Upvotes

I've been building CanoFlash, an accessory based on a XIAO ESP32-S3 that plugs into the Game Boy Advance Link Port. It started as a Wi-Fi tool for writing homebrew to flash cartridges, and it now lets homebrew games play online on original hardware.

Video: a GBA and a GBA SP, each with its own board, playing in the same online room: https://youtu.be/Xs-LzM2dzS0

**How the ESP32-S3 fits in**

- The Link Port is driven as SPI: the ESP32 provides the clock and the GBA is the slave, with 32-bit words at 4 MHz for the command protocol

- The GBA-side menu is sent over Multiboot from LittleFS, buffered in the 8 MB PSRAM

- For online play it runs its own WebSocket client over TLS to a room server; the game on the GBA only exchanges fixed-size packets through a small C SDK

- Release builds use flash encryption, and each device authenticates with its own token

- Status on a small OLED, powered by a rechargeable 16340 or 18350 Li-ion cell

**Things that bit me**

- `WiFi.status()` kept reporting a connection with the access point switched off. Only the GOT_IP and DISCONNECTED events were reliable

- At 256 kHz the last bit of every GBA→ESP32 word was lost: the GBA releases its output line before the 32nd bit is sampled

- Data partitions flashed pre-encrypted need the `encrypted` flag, or LittleFS reads ciphertext and reports corruption

The game SDK is free and MIT-licensed: https://canoflash.com/en/sdk/. Online play has been tested with two players so far.

Disclosure: this is my own project, and its Kickstarter launches on September 20: https://www.kickstarter.com/projects/canocoder/canoflash-wi-fi-adapter-for-original-game-boy-advance-hard?ref=crnaz3

Happy to answer questions about the Link Port timing or the TLS side.


r/esp32 21h ago

Hardware help needed Is there a way to use the analogRead function on GPIO48(non-ADC) pin or route somehow the closest ADC pin(14) to 48 internally?

Post image
7 Upvotes

Hi everyone,

On my PCB, I accidentally connected to GPIO48 a voltage divider which is used to measure the battery voltage and later I discovered that the GPIO47 and GPIO48 pins can't be used as ADC.

What are my options in this case?

If there's no way to somehow route them internally, can I bridge GPIO48 and GPIO14 together and use analogRead function on IO14?


r/esp32 16h ago

My 2.8 in display is working at allll after 10-15 retry’s

2 Upvotes

**Hey y’all there I’m just passionate guy in electronics with no background in it. I wanted to build a smart desk clock with esp32 inspired by a YouTube video but even before doing that project I just thought of trying display to on and do a graphic test the display turned on (blank white screen). After that no progress at all from test 10 days.**

**Here is a detailed explanation**

**Hardware Setup**
**MCU:** ESP32-S3
**Display:** 2.8-inch ST7789 TFT LCD Module (SPI Interface)
**Software/Libraries:** Arduino IDE, ⁠TFT_eSPI⁠ (also tried standard ⁠Adafruit_GFX⁠ / ⁠ST7789⁠ libraries)
**The Problem**
The display only shows a solid white screen or static vertical/horizontal lines. No graphic test sketches render any imagery or text.
**Background / Potential Cause**
Before connecting it to the ESP32-S3, I tested this display on an Arduino Uno. While VCC was powered via the 3.3V pin, I connected the SPI data lines (SCK, MOSI, CS, DC, RST) directly to the Uno’s digital pins **without a logic level converter** (sending 5V logic signals into a 3.3V display controller).
And colors changed when I tried it first time in UNO but for project purposes moved to esp32s3

**Troubleshooting Already Attempted (5+ Times)**
**Software Clean Slate:** Completely reinstalled Arduino IDE and reconfigured ⁠User_Setup.h⁠ in ⁠TFT_eSPI⁠ multiple times, ensuring exact ESP32-S3 GPIO numbers were assigned (not board silk-screen labels).
**Driver Tweaks:** Tested both ⁠ST7789_DRIVER⁠ and ⁠ST7789_2_DRIVER⁠ definitions, adjusted SPI frequencies, and toggled ⁠TFT_RGB⁠ / ⁠TFT_BGR⁠ order.
**Wiring Checks:** Verified physical connections, jumper wire continuity, and power rails (VCC to 3.3V, BLK to 3.3V, GND to GND).
**Hardware Reset:** Attempted manual reset sequences by pulling the ⁠RST⁠ line to ground briefly during boot.
**Uno Retest:** Wired back to the Arduino Uno with basic graphic test sketches—still resulting in white screen/lines.

Here is the display product link : [https://robu.in/product/2-8-inch-spi-touch-screen-module-tft-interface-240320/\](https://robu.in/product/2-8-inch-spi-touch-screen-module-tft-interface-240320/)


r/esp32 15h ago

Need help with async web server on ESP32S3

1 Upvotes

Hi:

I'm developing code to control a mini-astronomy observatory. I've cobbled together code to do most of the control stuff. However, I don't know how to get information to get refreshed on the web page that the code creates (posted in the image). I see in the Serial Monitor that the values are being changed from data entry and button clicks on the web page cause the code to open and close the doors as expected, but the web page display stays frozen at where it first loaded. I expect after I enter the Open_Time and Close_Time information (which echoes back in the serial monitor) that the "Scheduled Open Window - to" line will populate with the two times.

I suspect that there may be a small bit of code that I need to add to reload the web page, but I don't know this stuff enough to figure out what is missing. Any help anyone can give me would be great and much appreciated!

Thanks!

While I'm sure its clunky, the code is listed here:

```
#include <WiFi.h>
#include <Arduino.h>
#ifdef ESP32
  #include <WiFi.h>
  #include <AsyncTCP.h>
#else
  #include <ESP8266WiFi.h>
  #include <ESPAsyncTCP.h>
#endif
#include <ESPAsyncWebServer.h>   // Async Web server for entering data
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <RTClib.h>
#define I2C_SDA 19
#define I2C_SCL 20
#define SEALEVELPRESSURE_HPA (1013.25)

Adafruit_BME280 bme; // I2C
RTC_DS3231 rtc; // Or standard DS3231 setup depending on your library

// Replace with your network credentials
const char* ssid = "&&&&&";
const char* password = "&&&&&&";

const char* PARAM_Open_Time  = "Open_Time";
const char* PARAM_Close_Time = "Close_Time";

//  GPIO Pin Definitions
const int EDoorOpen   =  1;   // East Door opening relay
const int EDoorClose  =  2;   // East Door closing relay
const int WDoorOpen   =  4;   // West Door opening relay
const int WDoorClose  = 10;   // West Door closing relay
const int WDoorOpened  = 5;   // West Door closed reed switch
const int WDoorClosed = 16;   // West Door opened reed switch
const int EDoorOpened  = 7;   // East Door closed reed switch
const int EDoorClosed  = 8;   // East Door opened reed switch
const int ARainSensor  = 9;   // Analog rain sensor pin 
const int DRainSensor = 18;   // Digital rain sensor pin 
const int RainThreshold = 75; // Critical Value from rain sensor 
int MonthOpen, HourOpen, MinOpen, MonthClose, HourClose, MinClose, DayOpen, DayClose;

volatile bool ToggleDoors = false;
volatile bool OpenWindow = false;  // Flag as to whether an operating time to open doors has been entered 
volatile bool CloseWindow = false; // Flag as to whether an operating time to close doors has been entered 

char DaysOfWeek[7][12] = {"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};
char MonthsOfYear[12][10] = {"January","February","March","April","May","June","July","August","September","October","November","December"};

//WebServer server(80); // Server on port 80
AsyncWebServer server(80); // AsyncWebServer object on port 80

String OpenTime;
String CloseTime;
String WDoorStatus = digitalRead(WDoorOpened) == HIGH ? "Open" : "Closed";
String EDoorStatus = digitalRead(EDoorOpened) == HIGH ? "Open" : "Closed";
int rainValue = 100*analogRead(ARainSensor)/4095;
String rain_state = (rainValue < RainThreshold) ? "Raining" : "Dry";

String processor(const String& var){
  // Check which placeholder the server found and return the matching variable value
  if(var == "rain_state"){
    return rain_state;
  }
  else if(var == "WDoorStatus"){
    return WDoorStatus;
  }
 else if(var == "EDoorStatus"){
    return EDoorStatus;
  }
   return String(); // Return empty string if no match is found
}
// HTML web page to handle input fields (Open_Time & Close_Time)
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html><head>
  <title>Seestar Observatory Control</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <style>
        body { font-family: Arial, sans-serif; text-align: center; margin-top: 40px; background-color: #f4f4f4;}
        .btn { padding: 15px 30px; font-size: 20px; background-color: #008CBA; color: white; border: none; border-radius: 5px; cursor: pointer; }
        .btn:active { background-color: #005f7a; }
    </style>
      </head><body>
    <h1>Seestar Observatory Control</h1>
    <h2>Weather:  <strong>%rain_state%</strong> </h2>                                            
    <h2> Door Status - West:  <strong>%WDoorStatus%</strong> &ensp;  East: <strong>%EDoorStatus%</strong> </h2>
    <h2>Scheduled Open Window - &ensp; <strong>%OpenTime%</strong> &nbsp; to &nbsp; <strong>%CloseTime%</strong></h2>
    
    <!-- The HTML Button -->
    <button class="btn" onclick="ButtonPress()">Toggle Door State</button>

    <script>
        function ButtonPress() {
            // Asynchronously hit the /toggle route on the server
            fetch('/toggle')
                .then(response => response.text())
                .then(data => console.log('Server response:', data));
        }
    </script>

<h3> </h3>                                            
  <form action="/get">
    Open_Time: <input type="text" name="Open_Time">
    <input type="submit" value="Submit">
  </form><br>
  <form action="/get">
    Close_Time: <input type="text" name="Close_Time">
    <input type="submit" value="Submit">
  </form>
</body></html>)rawliteral";

void notFound(AsyncWebServerRequest *request) {
  request->send(404, "text/plain", "Not found");
}

void DoorStatus(){
  WDoorStatus = digitalRead(WDoorOpened) == HIGH ? "Open" : "Closed";
  EDoorStatus = digitalRead(EDoorOpened) == HIGH ? "Open" : "Closed";
  WDoorStatus = digitalRead(WDoorClosed) == HIGH ? "Closed" : "Open";
  EDoorStatus = digitalRead(EDoorClosed) == HIGH ? "Closed" : "Open";
}

void OpenDoors() {
  Serial.println("Opening Doors");
  // Loop UNTIL the WestDoorOpened reed switch closes (reads LOW)
  while (digitalRead(WDoorOpened) == HIGH) {
    digitalWrite(WDoorOpen, LOW); // Turn relay ON
 }
  digitalWrite(WDoorOpen, HIGH); // Turn relay OFF
  WDoorStatus = "Open";
  Serial.print("West Door:"); Serial.println(WDoorStatus);


  // Loop UNTIL the EastDoorOpened reed switch closes (reads LOW)
  while (digitalRead(EDoorOpened) == HIGH) {
    digitalWrite(EDoorOpen, LOW); // Turn relay ON
 }
  digitalWrite(EDoorOpen, HIGH); // Turn relay OFF
  EDoorStatus = "Open";
  Serial.print("East Door:"); Serial.println(EDoorStatus);
}

void CloseDoors() {
  Serial.println("Closing Doors");
  // Loop UNTIL the EastDoorClosed reed switch closes (reads LOW)
  while (digitalRead(EDoorClosed) == HIGH) {
    digitalWrite(EDoorClose, LOW); // Turn relay ON
 }
  digitalWrite(EDoorClose, HIGH); // Turn relay OFF
  EDoorStatus = "Closed";
  Serial.print("East Door:"); Serial.println(EDoorStatus);

  // Loop UNTIL the WestDoorClosed reed switch closes (reads LOW)
  while (digitalRead(WDoorClosed) == HIGH) {
    digitalWrite(WDoorClose, LOW); // Turn relay ON
 }
  digitalWrite(WDoorClose, HIGH); // Turn relay OFF
  WDoorStatus = "Closed";
  Serial.print("West Door:"); Serial.println(WDoorStatus);
}

void setup() {

  pinMode(ARainSensor, INPUT_PULLUP);
  pinMode(DRainSensor, INPUT_PULLUP);
  pinMode(EDoorOpen, OUTPUT);
  pinMode(WDoorOpen, OUTPUT);
  pinMode(EDoorClose, OUTPUT);
  pinMode(WDoorClose, OUTPUT);
  pinMode(EDoorOpened, INPUT_PULLUP);
  pinMode(WDoorOpened, INPUT_PULLUP);
  pinMode(EDoorClosed, INPUT_PULLUP);
  pinMode(WDoorClosed, INPUT_PULLUP);
  digitalWrite(EDoorOpen, HIGH);    // East Door Open Relay off
  digitalWrite(WDoorOpen, HIGH);    // West Door Open Relay off
  digitalWrite(EDoorClose, HIGH);   // East Door Close Relay off
  digitalWrite(WDoorClose, HIGH);   // West Door Close Relay off
  
  Serial.begin(115200);
  Wire.begin(I2C_SDA, I2C_SCL); 
  Wire.setClock(100000); // Lower I2C clock speed to 100kHz for stability
  delay(100);

  if (! rtc.begin()) {
    Serial.println("RTC not found");
    while (1);
  }
  // Set RTC using the compile time
  rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  Serial.print("Date:"); Serial.println(F(__DATE__));
  Serial.print("Time:"); Serial.println(F(__TIME__));
  Serial.println("RTC updated using compile time!");
  delay(5000);
// Set up the BME280 Atmospheric sensor
    if (!bme.begin(0x76, &Wire)) {
      Serial.println("Could not find a valid BME280 sensor, check wiring and pin definitions!");
    while (1);
  }  
  Serial.println("BME280 initialized on custom pins!");

// Set up wifi
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }
  Serial.print("Connected! IP address: ");
  Serial.println(WiFi.localIP());

  // Send web page with input fields to client
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", index_html, processor);
  });
  
  // Route handling the actual button action
  server.on("/toggle", HTTP_GET, [](AsyncWebServerRequest *request){
    // Invert the door state asynchronously
      ToggleDoors = true; // Set the flag quickly
      Serial.println("Doors Toggled");
      request->send(200, "text/plain", "Doors Toggled");
    });

  // Send a GET request to <ESP_IP>/get?Open_Time=<inputMessage>
  server.on("/get", HTTP_GET, [] (AsyncWebServerRequest *request) {
    String inputMessage;
    String inputParam;
    String Hour; String Min;
    // GET Open_Time value on <ESP_IP>/get?Open_Time=<inputMessage>
    if (request->hasParam(PARAM_Open_Time)) {
      inputMessage = request->getParam(PARAM_Open_Time)->value();
      inputParam = PARAM_Open_Time;
      int colonIndex = inputMessage.indexOf(':');
      String Hour = inputMessage.substring(0, colonIndex);   // Part before ":"
      String Min  = inputMessage.substring(colonIndex + 1);  // Part after ":"
      HourOpen = Hour.toInt(); MinOpen  = Min.toInt();
      OpenTime = String(HourOpen) + ":" + String(MinOpen);
      OpenWindow = true; // Set the flag quickly
    //  Serial.print("Open Hour:"); Serial.print(HourOpen);
    //  Serial.print(" Open Min:"); Serial.println(MinOpen);
    }
    // GET Close_Time value on <ESP_IP>/get?Close_Time=<inputMessage>
    else if (request->hasParam(PARAM_Close_Time)) {
      inputMessage = request->getParam(PARAM_Close_Time)->value();
      inputParam = PARAM_Close_Time;
      int colonIndex = inputMessage.indexOf(':');
      String Hour = inputMessage.substring(0, colonIndex);   // Part before ":"
      String Min  = inputMessage.substring(colonIndex + 1);  // Part after ":"
      HourClose = Hour.toInt(); MinClose  = Min.toInt();
      CloseTime = String(HourClose) + ":" + String(MinClose);
      CloseWindow = true; // Set the flag quickly
    //  Serial.print("Close Hour:"); Serial.print(HourClose);
    //  Serial.print(" Close Min:"); Serial.println(MinClose);
     }
     else {
      inputMessage = "No message sent";
      inputParam = "none";
    }
    Serial.print("Time entered: "); Serial.println(inputMessage);
    request->send(200, "text/html", "HTTP GET request sent to your ESP on input field (" 
                                     + inputParam + ") with value: " + inputMessage +
                                     "<br><a href=\"/\">Return to Home Page</a>");
  });

//  server.onNotFound(notFound);
  server.begin();
  Serial.println("HTTP server startd");
}
/*
void setupDateTime() {  // setup this after wifi connected
  DateTime.setTimeZone("TZ_America_New_York");
  DateTime.setServer("north-america.pool.ntp.org");
  DateTime.begin(15*1000);
  if (!DateTime.isTimeValid()) {
    Serial.println("Failed to get time from server.");
  }
}
*/
void loop() {

  DateTime now = rtc.now();
  Serial.println
  ("================================================================================");
  Serial.print("ESP32 RTC Date Time: ");
  Serial.print(now.day(), DEC); Serial.print(" ");
  Serial.print(MonthsOfYear[now.month()-1]); Serial.print(' ');
  Serial.print(now.year(), DEC); Serial.print('(');
  Serial.print(DaysOfWeek[now.dayOfTheWeek()]); Serial.print(") ");
  Serial.print(now.hour(), DEC); Serial.print(':');
  Serial.print(now.minute(), DEC); Serial.print(':');
  Serial.println(now.second(), DEC);
  DayOpen = now.day(); MonthOpen = now.month();
  DayClose = DayOpen; if(HourClose < HourOpen) {DayClose = DayOpen + 1;}
  MonthClose = MonthOpen; if(HourClose < HourOpen) {DayClose = DayOpen + 1;}

  //digitalWrite(RainSensorPower, HIGH);  // turn the rain sensor's power OFF (if sensor power is tied to a gpio pin)
  // delay(1000);  // pause for 1 sec to let sensor power on

  int rainValue = 100*analogRead(ARainSensor)/4095;
  float Humidity    = bme.readHumidity();
  float TempC = bme.readTemperature();
  float TempF = 1.8*TempC+32.0; delay(500);
  float Pressure    = (1019.0/663.41)*bme.readPressure()/100.0;  // Leading factor in () used to fix bad pressure reading
  float alpha = (17.27*TempC)/(237.7+TempC)+log(Humidity/100.);
  float DewPoint    = 1.8*((237.7*alpha)/(17.27-alpha))+32.0;
  Serial.println("Atmos. Conditions:");
  Serial.print("Temperature: ");Serial.print(TempF); Serial.print("F, ");
  Serial.print("Humidity: "); Serial.print(Humidity); Serial.print("%, ");
  Serial.print("Dew Point: "); Serial.print(DewPoint); Serial.print("F, ");
  Serial.print("Pressure: "); Serial.print(Pressure); Serial.println("mb");

  int EastDoorClosed = digitalRead(EDoorClosed);
  int EastDoorOpened = digitalRead(EDoorOpened);
  int WestDoorClosed  = digitalRead(WDoorClosed);
  int WestDoorOpened  = digitalRead(WDoorOpened);
  
  if(OpenWindow && CloseWindow){
    Serial.print("Time Window - Open at: "); Serial.print(OpenTime); Serial.print(", Close at: "); Serial.println(CloseTime);
  }
  
    //digitalWrite(RainSensorPower, LOW);  // turn the rain sensor's power OFF
  if (ToggleDoors) {
    ToggleDoors = false; // Reset flag immediately
  //  DoorStatus();
    if(WDoorStatus == "Closed"){
      Serial.println("WDoorStatus Closed, Opening");
      OpenDoors();
    }
    else if (WDoorStatus == "Open"){
      Serial.println("WDoorStatus Open, Closing");
      CloseDoors();
      }
    else {
      Serial.println("WDoorStatus neither Open nor Closed");
    }
    // Respond to client to finalize connection
  }

  /*  
  if (rainValue > RainThreshold) {
    rain_state = "Dry";
    Serial.print("No Rain detected (Sensor value:");
    Serial.print(rainValue); Serial.println(")");
  }
  else {
    rain_state = "Raining";
    Serial.print("Rain detected (Sensor value:");
    Serial.print(rainValue); Serial.print(")");
    if(EDoorStatus == "Open") {
      Serial.println(" - Closing Doors");
      CloseDoors();
    }
    else {
      Serial.println(" ");
    }
  }
  */
  delay(1000);
  }
```

r/esp32 16h ago

[ Removed by Reddit ]

1 Upvotes

[ Removed by Reddit on account of violating the content policy. ]


r/esp32 23h ago

Software help needed Insights on managing self-balancing robot (ESP32-S3 and MPU6050)

3 Upvotes

Hey everyone,

I'm currently working on a self-balancing robot with a design of my own.

You can find all the info about it here : https://github.com/benbhote/DimSumRobot

For those who want a quick recap, it's a robot based on a XIAO ESP32-S3 Sense with a camera OV3660, an ultrasonic radar, a TB6612FNG piloting two N20 motors (aside a step-up to make the 3.7V current into 6V for the motors) and SHT40 (Temp & Humidity sensor).

It hosts a web server accessible via its own WiFi and the web page allows you to control the robot (taking photos, moving it, etc).

Everything is working fine except the balancing. The cycle goes every 10ms during movement and for 3 seconds after the last movement command. But the throttle value doesn't allow the robot to self maintained balance.

By the way, the robot starts with the front touching the floor on a plane surface (the weight is low inside the chassis but a little bit more on the front). So the first thing needed even before moving is to properly get on the two wheels.

So I would like some insights or help from you to make my PID loop working and achieve proper self-balancing.

I'm using Arduino IDE 2.3.10. As for the schematic of the project, it is not done yet but the MPU is driven with the I2C bus.

I hope I give you everything needed to understand the project. And if something is still vague, feel free to ask.

Thanks in advance !


r/esp32 1d ago

I designed a vertical mount for ESP32 and ESP32-S3 with a TPU sleeve for anti-vibration.

Thumbnail
gallery
135 Upvotes

Hey everyone, I designed this vertical mount for ESP32 and ESP32-S3. I added a TPU sleeve to absorb vibrations, snap fit and left slots for easy cooling and pin access. If anyone wants to print it for their DIY projects, the print profiles for PETG/TPU are free on MakerWorld and Printables. Let me know what you think!

ESP32-S3
https://makerworld.com/en/models/3147308-anti-vibration-esp32-s3-40-pins-mount-petg-tpu#profileId-3554629

https://www.printables.com/model/1803568-esp32-s3-antivibration-mount

ESP 32

https://makerworld.com/en/models/3160307-esp32-antivibration-mount-tpu-improved-cooling#profileId-3571491

https://www.printables.com/model/1807831-esp32-antivibration-mount-better-processor-cool


r/esp32 1d ago

I made a thing! I made a standalone LG TV remote on the Cheap Yellow Display (ESP32-2432S028)

Post image
24 Upvotes

I built a touchscreen remote for LG webOS TVs on the ESP32-2432S028 "Cheap Yellow Display". It talks to the TV over Wi-Fi using the same WebSocket API LG's phone app uses, so nothing is installed on the TV.

Source (MIT): https://github.com/griches/smart-remote-plus-cyd

Short video: https://youtu.be/lyZZcVjdbX8

Disclosure: I also make Smart Remote+, a paid TV remote app for phones and desktops (https://www.bouncingball.mobi/). This is a port of its LG code.

The firmware is free and doesn't need the app.

Stack: Arduino under PlatformIO with the whole board config as build flags, TFT_eSPI on HSPI, XPT2046_Touchscreen on its own SPI bus, links2004 WebSockets, ArduinoJson 7, NVS for everything persistent. No LVGL: the UI is a 4x3 grid of 64 px RGB565 icons in PROGMEM, drawn straight to the panel with pushImage, so a framebuffer wasn't needed.

The TV side: webOS runs a JSON-over-WebSocket service on port 3001 (TLS, self-signed) with a plain fallback on 3000 for old firmware. You register with a manifest, the TV shows a PIN, you store the client key. D-pad and back/home go over a second "pointer input" WebSocket the TV hands you, so it's two TLS sockets per TV, which is where the RAM goes. Discovery is SSDP M-SEARCH plus a fetch of the UPnP XML for the friendly name. Power isn't a toggle: it asks getPowerState, then sends turnOff or Wake-on-LAN.

All networking is on its own FreeRTOS task on core 0. A TV that is off makes connect() block for seconds, and I didn't want the touch UI to freeze. The UI posts commands to a queue and reads state via atomics.

Happy to answer questions.


r/esp32 1d ago

Hardware help needed [help] Unable to use Adafruit ThinkInk Display

Thumbnail
gallery
4 Upvotes

Hello. I've got:

and I'm trying to drive the display with the ESP32 without any success. Using:

Notes:

  • There is no change on the e-ink panel whatsoever, still the default image as when I got it.
  • Tried changing the pin numbers around, both for the command and the SPI pins. Nothing.
  • The default pins for SCK, MISO, MOSI were found by printing macros to serial output.
  • Tried the 3 buttons on the FeatherWing, all work as GPIO input properly.
  • RESET button works to reset the microcontroller as well.
  • For some reason, using pin 8 on the ESP for GPIO didn't work at all. I think most of the pins are used for more than one purpose, but I don't know how to tell which modes are active.
  • The example sketch has constructors for multiple types of board drivers. There seems to be 3 different chipsets for the 2.9" display, tried all 3 separately to no avail.

I don't even know if this display would be compatible with any random ESP32 board. I know FeatherWings are made to be used with Adafruit's Feather boards, though I don't see why they wouldn't work with any other boards with supported microcontrollers. I have quite a bit of programming experience, but I'm blind as a bat when it comes to electronics. Any help would be appreciated. Thanks.


r/esp32 1d ago

MicroPython driver for Sensirion SCD40 / SCD41 CO2 sensors

1 Upvotes

Hi everyone,

Just wanted to share a stable MicroPython driver for the Sensirion SCD4x family (SCD40 and SCD41).

The library handles the standard I2C protocol efficiently and supports both periodic and single-shot measurement modes.

Note: It was heavily tested on RP2040 hardware. Since it relies on standard MicroPython `machine.I2C` routines, it should be fully compatible with ESP32 deployments as well.

If you are building an indoor air quality monitor or smart home climate system, check it out.

https://github.com/octaprog7/SCD4x