Cutie Signal: Wifi SSID Compliments (ESP32)

by ClickityClick in Circuits > Arduino

313 Views, 2 Favorites, 0 Comments

Cutie Signal: Wifi SSID Compliments (ESP32)

WiFiCutieSignal.jpg

Welcome to the "Cutie Signal" project, a unique way to say sweet things that leverages the WiFi, AP (Access Point) capability of a Heltec ESP32 WiFi LoRa 32 (V3) module.

The WiFi can be used to broadcast a different SSIDs every day. Each SSID (day) change will deliver a new cute compliment to a special someone between 17:00 to 22:00 (local time).

The aim is to keep this system running for around 60 days (so that's 60+ compliments). To do this it will require approximately 100,000+ mAh of battery power and require that the ESP32 is put into a deep sleep when outside of it's ON or braudcast hours.

The batteries and ESP are housed in a waterproof box and placed near my intended recipients walking route so they can easily see the signal without inconvenience and get a nice compliment each day as they walk by.

The recipient can connect to the Access Point, which will load an (on-chip) web page containing something nice. In my case the page has a Cutie Code which they will need to continue a quest like adventure as it part of a bigger game.

You can of course do anything you want when making your own version of this.

Supplies

box.png
Power Bank.png
BatteryAlwaysOn.png
Locks.png
BatteriesINR18650.png
ESP32-V3.png

4 x Power Banks of 30,000 mAh (They say 52,000mAh but it is a lie -- come on Amazon)

1 x Battery Shield (Need to act as a buffer against the Power Banks auto off)

4 x INR18650 batteries (will only need two but they come in packs of 4)

1 x INR18650 Battery Charger (Optional as the Battery Shield will charge it for you as well)

1 x MEIJIA Waterproof Case

1 x Heltec ESP32 WiFi LoRa 32 (V3)

4 x USB to USBC short cables

2 x Locks (to lock the MEIJIA Waterproof Case)

Physical Setup

IMG20240718235213.jpg
IMG20240718235221.jpg
IMG20240718235229.jpg

The Power Requirements


As stated the goal was to provide 5 hours of active WiFi from a battery pack with the remaining time keeping the ESP32 in Deep Sleep Mode. This is so that it should be able to live for 60+ days as I don't know when the player will exactly get to that part of the adventure.

When in ESP32 is in WiFi full power consumption it is expected to consume ~250 mA give or take.

Over 5 hours it will run daly that is approximately 1250 mAh (per day) ... lets add an error margin and say it is 1500 mA per day for other losses.

Looking for 60 days of operation that's (1500 mAh * 60) requiring around 90,000 mAh minimum. The four battery banks are 30,000 mAh each so provide a total of 120,000 mAh. You can add a little extra from the Battery Shield ( 2*3500 mAh) which as a 7000 mAh capacity. Please not that the Battery Banks i purchased from Amazon said 52,800 mAh but checking them when they arrived they are only 30,000 mAh hence had to use 4 not two.

In any case the overall total is approximately 127,000 mAh of available power. Remember there will be losses in the power system and all the other various bits of electronics so better to over shoot with the battery capacity.

Some references worth looking at to know more on the sleep power consumption of the ESP32 are provided below. I would recommend having a look.

Deep sleep consumes about ~(10-150) µA so consumes 2.85 mA per day (150µA*19Hours) or 171 mA over 60 days. When compared to 1500 mA per day when active deep sleep mode can pretty much be ignored.

Given the Battery Shield has 4 bright LEDs on it (to show battery charge levels) each potentially using up to 20mA each this is a problem.

These LED would consume 80mA continuous or 1920 mA per day. That would be more than the EPS so they will need to be removed from the board. In my case I just pulled them off with a pair of scissors or pliers but you could eloquently de-solder them if you wish. You can then just use a voltmeter to check their status if you need to.


ESP32 Sleep Modes Ref 1: https://lastminuteengineers.com/esp32-deep-sleep-wakeup-sources/

ESP32 Sleep Modes Ref 2: https://www.programmingelectronics.com/esp32-deep-sleep-mode/

ESP32 Sleep Modes Ref 3: https://deepbluembedded.com/esp32-sleep-modes-power-consumption/


Grrr to Power Banks

For some reason all power banks auto turn off if the current draw is not significant. Hence, when you put the ESP32 in Deep Sleep it uses only 10-150 µA and so the Power Banks turn off and it then switches off the ESP32. Not a good thing.

The easiet was I found to solve this was to add the Battery Shield. The Battery Shield has the option to set the power to 'OLD' which means when you turn the unit on it will remain on which is what is required.

Then you can daisy chain the other Power Banks with the final one feeding the Battery Shield's USBC port which will keep it charged. The USB output of the Battery Shield will supply power to the ESP32.

Ensure all batteries are fully charged before you deploy in this configuration.

With this setup the Power Banks charge each other in the daisy chain fashion and eventually the Battery Shield which as it is always (acts as a buffer) to the ESP32 Shield.

This was the best way I could find to use off the shelf components that would keep the ESP32 running without having to make a custom power circuit for the power banks.

You can see from the photos how to cut out the foam to get it to all fit into the the water proof MEIJIA case. I have not done a huge amount of testing to see how water proof the case it other than fill a sink a push it under.

I will wrap the case in a another bag before burying it but that's just to keep it clean. Will want to recover all the bits after the power runs out.

Software Setup

pippa-programming.jpg

The Software Bit


The first step is to install the Arduino IDE. In this case an older version 1.8.19 as you will need a plugin which is currently not working with the IDE version 2.

You will need to install SPIFFS plugin.

The information required for that can be found here:

https://randomnerdtutorials.com/install-esp32-filesystem-uploader-arduino-ide/

That said there is now a new option called LittleFS (filesystem) you can use instead if you wish to stick with IDE 2+ but as I did this with SPIFFS and IDE 1.8.19 will continue with that.

Before we go too far I want to give credit to the following Github accounts as I used their code as basis for learning the core of what was necessary for programming the ESP32. These two code repositories and are a good place to look before you get started with this.

I will of course share my code also so you can copy paste, tweak and improve it.

It took me about two to three weeks to figure it all out with a modest amout of testing.


My code for the Cutie Signal can be found here: https://github.com/smoking-electronics/CutieSignal/tree/main


As the program uses the internal RTC (Real Time Clock) you have to keep power supplied to the board if you wish to maintain time. There can be no power interruptions. Note that there will be some clock drift but it will be minor over the 60 days of expected operation. The RTC is key to success of this project as it can provide RAM that persist across restarts but not during power outages.

As such the RTC provides memory which is used to hold state when the ESP32 is in deep sleep. This is held in the following variables. Again if power is lost these variables will be reset (as if booting from scratch). RTC_DATA_ATTR is memory on the RTC module.


RTC_DATA_ATTR int bootCount    =  0;
RTC_DATA_ATTR int timeFlag      = 0;
RTC_DATA_ATTR int current_line  = 0;
RTC_DATA_ATTR int dayNow        = 0;

As a point of note when the program comes out of deep sleep the program will operate as if it was power cycled so will run the setup function again.

As long as power is maintained the memory from the RTC can be used to keep state. In this case the day, index of the SSID , number of boots and a timeFlag for first boot (althought you could probably use bootCount too). The RTC will keep time.


String never_gonna[] = {...}

The "never_gonna" array holds the different SSIDs that will be displayed over the dayly cycles.

Replace with your own String but ensure that each is less than 32 Characters. Kept the "never_gonna" array name as a hommage to the rickroll example that gave me the initial idea for all this.


 rtc.setTime(01, 37, 17, 11, 7, 2024);  

You need to set this at the compile time to match your current local time. Then upload and run it as quick as possible. If your board resets due to loss of power it will reset to this time and mess everthing up in terms of time sync.

Hence keeping power on is key to the success of this project (if you care about time). You could use an external RTC which would keep time no matter (assuming it has its own coin cell battery back up) but it would require more boards and wiring so didn't go for that. Better to squeese out what you can from the ESP.


Other Core Functions


 // Routes to load media content
 server.on("/media/pippa.webp", HTTP_GET, [](AsyncWebServerRequest *request) {
request->send(SPIFFS, "/media/pippa.webp", "image/webp");
 });

When you add files for the webpage component you need to add a route to them with the right mime type. If you forget to do this for any file it won't work !! Remember this!!


int secondsToSleep(int currentHour, int currentMinute) {
       if (currentHour == 16) {
           //return only minutes
           int minutes = 60 - currentMinute;
           return minutes * 60; //returns seconds
       }

       if (currentHour >= 22 ) {
           if (currentHour == 22) {
               int minutes = 60 - currentMinute;
               return (minutes * 60) + 3600 + 61200; //returns seconds
               //3600 is 1 hr, 61200 is 17 hrs
           }

           if (currentHour == 23) {
               int minutes = 60 - currentMinute;
               return (minutes * 60) + 61200; //returns seconds
           }
       }

       if (currentHour >= 0 && currentHour < 17 ){
           int hours = 16 - currentHour;
           int minutes = 60 - currentMinute;
           return (hours * 60 * 60) + (minutes * 60);
       }
       return 0; //0 = should be active
}

This function returns the number of seconds that the ESP32 should remain asleep. If the answer is 0 then the ESP32 should be active (ON). Please bear in mind that this is specifically calculating for ON operation between 17:00 and 22:00. You will need to modify this function if you want different time windows.




 if (numSeconds == 0) {

       //This just updates the OLED display every 5 seconds
       if( millis() >= time_1 + INTERVAL_T1) {   
             time_1 +=INTERVAL_T1;                      
                   showText("Cutie Signal. SSID: '" + never_gonna[current_line] + "'. "+rtc.getTime("%A, %B %d %Y %H:%M:%S")+ " Min HEAP: "+String(esp_get_minimum_free_heap_size()));
       }

 }else{
        server.end(); //Close down the Weberver gracefully if possible.                            
        delay(2000);
       //If sleep is less than 30 min use that other sleep in 30 min chunks due to INT limits.
       if (numSeconds > 1800 ) {                 
               showText("Sleep for "+String(numSeconds)+" seconds 30 min chunks:: "+rtc.getTime("%A, %B %d %Y %H:%M:%S")+ " Min HEAP: "+String(esp_get_minimum_free_heap_size()));
               delay(1000);
               esp_sleep_enable_timer_wakeup(1800 * 1000000); //Sleep for 30 min (1800 sec) and repeat until less than 1800 remains. Then sleep what remains.
               esp_deep_sleep_start();
       }else{               
               showText("Sleep for "+String(numSeconds)+" seconds:: "+rtc.getTime("%A, %B %d %Y %H:%M:%S")+ " Min HEAP: "+String(esp_get_minimum_free_heap_size()));
               delay(1000);
               esp_sleep_enable_timer_wakeup(numSeconds * 1000000); //
               esp_deep_sleep_start();
       }                       
 }

This section runs in the main loop and continually checks if the ESP32 should be asleep or awake.

Note, as the largest number you can give the deep sleep method is about 30 minutes (1800 seconds) as the max number you can pass it is 2147483647 uS.

Please read this for more detailed information on this:

The same sleep function is in the setup so that you don't have to go through the full boot before going back to deep sleep as this check will have to happen every 30 minutes. You will see a bried flash of the OLED as it updates before shutting off to back into deep sleep.


       //This just updates the OLED display every 10 seconds
       if( millis() >= time_1 + INTERVAL_T1) {   
             time_1 +=INTERVAL_T1;                      
                   showText("Cutie Signal. SSID: '" + never_gonna[current_line] + "'. "+rtc.getTime("%A, %B %d %Y %H:%M:%S")+ " Min HEAP: "+String(esp_get_minimum_free_heap_size()));
       }

When active (on between 17:00 and 22:00) this section of code will update the OLED on the ESP32 with the SSID, time and amount of memory free on the heap space. If your HEAP gets too low you might have a memory or resource issue. I have tested this for a week or so and so far so good. As it effectively reboots every day not so much an issue. If too many people were to connect to the AP at the same time that might cause a problem.


 // Start AP with localIP, dnsIP, and gatewayIP
 WiFi.mode(WIFI_AP);
 WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));

 Serial.println("Setting SSID to '" + never_gonna[0] + "'.");
 WiFi.softAP(never_gonna[0].c_str());

 // Start DNS server with * (will redirect all)
 if(!dnsServer.start(DNS_PORT, "*", apIP)) {
   Serial.println("An error has occurred while starting DNS");
   return;
 }

This section is to setup up the AP (Access Point) with no password and using an SSID from the array, intially index 0.

This will allow the user to connect to the WiFi and then be taken to sign In page where you can have your contnet (index.html) displayed.

On the code front this should be all you need to get you going on the main parts at least.

Active Mode

pippa-dig-success.jpg
SSID-Active-OLED.png
APWebPage.jpg
SSID-Signal.jpg

In active mode when the SSID is broadcasting you will see all the details on the OLED screen. You will also of course be able to see the SSID being broadcast via your mobile or laptop WiFi in the regular way.

In the images you can see the SSID is availabe and if your connect you are taken to the Sign In page which is the on-chip index.html of your own creation. Edit how you wish!!

I hope you enjoy doing this project and that who ever you put this time and effort in for appreciates it.


Disclaimer Bits


I would suggest not using peoples real names, photos or anything identifiable in any of the content of the webpages or SSID's. Remember that this is open and presumably within a reasonable distance of their location. Just be sensible on that front !! Also if you don't know them personally it might be a creepy for them to see their real name on wild WiFi signal so think on that front too.