I just wanted to fix the first tracker's RTC. I was weak - couldn't help myself, the ESP32 was so tempting.
So you have a nice bit of code, how do we get from our nice ESP8266 program to one that will work on the ESP32 board ? Well the good news is that it largely works off the shelf but this depends on how far down the rabbit hole you went. So lets look at the boards and differences and show a real world example gets from A to B. There is a link at the end where you can if course download both projects from github and compare them to see what exactly changed.
Header FilesThe ESP8266 has it's own set of stuff where as the ESP32 is relying on more arduino native code.
So at the start of the program
#include <ESP8266WiFi.h>
#include <WiFiUDP.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <ESP8266HTTPUpdateServer.h>
#include <ESP8266httpUpdate.h>
ESP8266WebServer server(80);
ESP8266WebServer OTAWebServer(81);
ESP8266HTTPUpdateServer OTAWebUpdater;
Becomes this
#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <WiFiUDP.h>
WebServer server(80);
So rather than use the OTA updater object this was combined on the one web server and port.
There is one area where I ventured to the bottom of the rabbit hole and that's on the inbuilt ESP object, the ESP8266 one is bristling with stuff
class EspClass {
public:
// TODO: figure out how to set WDT timeout
void wdtEnable(uint32_t timeout_ms = 0);
// note: setting the timeout value is not implemented at the moment
void wdtEnable(WDTO_t timeout_ms = WDTO_0MS);
void wdtDisable();
void wdtFeed();
void deepSleep(uint64_t time_us, RFMode mode = RF_DEFAULT);
uint64_t deepSleepMax();
bool rtcUserMemoryRead(uint32_t offset, uint32_t *data, size_t size);
bool rtcUserMemoryWrite(uint32_t offset, uint32_t *data, size_t size);
void reset();
void restart();
uint16_t getVcc();
uint32_t getFreeHeap();
uint32_t getChipId();
const char * getSdkVersion();
String getCoreVersion();
String getFullVersion();
uint8_t getBootVersion();
uint8_t getBootMode();
uint8_t getCpuFreqMHz();
uint32_t getFlashChipId();
//gets the actual chip size based on the flash id
uint32_t getFlashChipRealSize();
//gets the size of the flash as set by the compiler
uint32_t getFlashChipSize();
uint32_t getFlashChipSpeed();
FlashMode_t getFlashChipMode();
uint32_t getFlashChipSizeByChipId();
uint32_t magicFlashChipSize(uint8_t byte);
uint32_t magicFlashChipSpeed(uint8_t byte);
FlashMode_t magicFlashChipMode(uint8_t byte);
bool checkFlashConfig(bool needsEquals = false);
bool flashEraseSector(uint32_t sector);
bool flashWrite(uint32_t offset, uint32_t *data, size_t size);
bool flashRead(uint32_t offset, uint32_t *data, size_t size);
uint32_t getSketchSize();
String getSketchMD5();
uint32_t getFreeSketchSpace();
bool updateSketch(Stream& in, uint32_t size, bool restartOnFail = false, bool restartOnSuccess = true);
String getResetReason();
String getResetInfo();
struct rst_info * getResetInfoPtr();
bool eraseConfig();
inline uint32_t getCycleCount();
Compared to the ESP32's class
public:
EspClass() {}
~EspClass() {}
void restart();
uint32_t getFreeHeap();
uint8_t getChipRevision();
uint8_t getCpuFreqMHz(){ return CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ; }
uint32_t getCycleCount();
const char * getSdkVersion();
void deepSleep(uint32_t time_us);
uint32_t getFlashChipSize();
uint32_t getFlashChipSpeed();
FlashMode_t getFlashChipMode();
uint32_t magicFlashChipSize(uint8_t byte);
uint32_t magicFlashChipSpeed(uint8_t byte);
FlashMode_t magicFlashChipMode(uint8_t byte);
bool flashEraseSector(uint32_t sector);
bool flashWrite(uint32_t offset, uint32_t *data, size_t size);
bool flashRead(uint32_t offset, uint32_t *data, size_t size);
uint64_t getEfuseMac();
So where we used these it was either replace or delete. This is most visible in the "node info tab" which took a big trim. The one item which did need and depend on was getChipId(), I'm fond of this as a modifier for config IP addresses. Saves all the devices piling up on the same address.
WiFi - Client connections - GrrrrOk this was a surprise and not in a good way... The ESP8266 is brilliant at maintaining client connection to an AP even if the router dies, reboots or is abducted by aliens. The ESP32 not so much ! Seems I'm not Robinson Curso here either, in spite of wanting this wrapped up by Friday. Being pragmatic about it I looked at what others had done and resolved to have a rude hack that would hopefully keep the thing connected. I hate putting "flying elephant shielding" in projects as its hard to prove it works. The mere absence of crashed elephant bodies during testing does not always mean the code protecting you.
if (!WiFi.isConnected()) {
lTD = (long)lTimeNext-(long) millis() ;
if (( abs(lTD)>40000)||(bPrevConnectionStatus)){ // trying to get roll over protection and a 30 second retry
lTimeNext = millis() - 1 ;
}
bPrevConnectionStatus = false;
if ( lTimeNext < millis() ){
Serial.println(String(buff )+ " Trying to reconnect WiFi ");
WiFi.disconnect(false);
// Serial.println("Connecting to WiFi...");
WiFi.mode(WIFI_AP_STA);
if ( ghks.lNetworkOptions != 0 ) { // use ixed IP
WiFi.config(ghks.IPStatic, ghks.IPGateway, ghks.IPMask, ghks.IPDNS );
}
if ( ghks.npassword[0] == 0 ) {
WiFi.begin((char*)ghks.nssid); // connect to unencrypted access point
} else {
WiFi.begin((char*)ghks.nssid, (char*)ghks.npassword); // connect to access point with encryption
}
lTimeNext = millis() + 30000 ;
}
}else{
bPrevConnectionStatus = true ;
}
This code fragment required lots of "plug kicking" to test. I think I got all the combinations sorted. I have also ported this back to two other projects and they appear to be doing better with regards to being on the line.
IO PinsThis is obviously different and I do find nutting this out tedious. The boards are generally marked with actual GPIO numbers on the ESP32's however finding the on board LED is difficult unless you have a circuit.. One nice thing about the ESP32 is it seems to have a sort of I/O matrix crossbar that allows you to route hardware signals within reason.
analogWrite() - Doesn't exist ! You cant just create a PWM pin as a one liner, you have to setup first.
#define RELAY_XZ_PWM 1
#define RELAY_YZ_PWM 2
...
if (tv.iOutputType<2) {
ledcSetup(RELAY_XZ_PWM, 5000, 10); /* Setup channels 5 kHz PWM, 10-bit */
ledcSetup(RELAY_YZ_PWM, 5000, 10);
ledcAttachPin(tv.RELAY_XZ_PWM, RELAY_XZ_PWM); /* assign PWM pins to channels */
ledcAttachPin(tv.RELAY_YZ_PWM, RELAY_YZ_PWM);
}
Then you go on to use the "ledcWrite(, )" in the same way as you would analogWite (). However its not the PIN it's the PWM channel your changing, subtly different way of working the tools. I've used 10 bit only so it's compatible with the ESP8266 project.
Web InterfaceFrom programming perspective almost no change but prolly due to my lack of experience I needed to share the web server between OTA and the main interface. I could not seem to get a secondary server running on another port, path of least resistance share the resource. The OTA did work fine, have already used it a few times when it is not convenient to hook on the USB cable.
Other LibrariesFortunately these all just worked... tip my hat to the people involved in writing them.
#include <TimeLib.h> // arduino standard book of spells
#include <Wire.h> // arduino inbuilt
#include <EEPROM.h> // arduino inbuilt
#include <stdio.h> // arduino inbuilt
#include <LSM303.h>
#include <L3G.h>
#include <SFE_BMP180.h> // Sparkfun github
#include <TinyGPS.h>
#include "ht16k33.h"
#include "SSD1306.h"
#include "SH1106.h"
#include "SH1106Wire.h"
#include "ds3231.h"
This is always a sticking point however I did try and pick stuff that would be ESP32 compatible when I originally wrote my ESP8266 programs. I know that sounds like a "cop out" but sometime some foresight is required in the game.
UDP stuffAll the same, the NTP code worked first time before I could say is the RTC set. However there is a nasty gotcha. If your DNS is not working and you make the UDP call by name not IP (aka REBOOT time). The problem is in the bowels of the UDP class which makes a different DNS resolution call to the general WiFi class one. So I was left with two choices, patch the library or drink more red wine and work around. So the fragment below seemed to be a bit more bullet proof. ie Use "WiFi.hostByName" before you do "beginPacket" and definitely don't "ask for it by name".
if (WiFi.isConnected()) {
WiFi.hostByName(address, ntpIP);
snprintf(buff, BUFF_MAX, "%u.%u.%u.%u\0", ntpIP[0], ntpIP[1], ntpIP[2], ntpIP[3]);
if (( ntpIP[0] == 0 ) && ( ntpIP[1] == 0 ) && ( ntpIP[2] == 0 ) && ( ntpIP[3] == 0 )){
Serial.println("No DNS - no point 0.0.0.0 ");
}else{
ntpudp.beginPacket(buff, 123); //NTP requests are to port 123 103.38.121.36
ntpudp.write(packetBuffer, NTP_PACKET_SIZE);
ntpudp.endPacket();
Serial.println("Sent NTP packet to "+String(address)+ " -> "+String(buff));
}
}else{
Serial.println("No WiFi - no point trying to send NTP packet...");
}
Long Routines and WatchdogsIt had to happen, the main web page routine was huge and got bitten by the dog. So after overcoming my inertial laziness and failing to discover how to reset the watchdog in CPU(0), it was page separation time. Shortly after there was a much more readable and logical project which now ran fine on all pages without rebooting the CPU when displaying the main web page.
Additional programming was put in (stolen from my ESU project) to finally allow the output pins on the tracker to be remapped from the user interface. Don't really know why it took so long to do these but it's now done ! Actually it's prolly due to a lack of choice in pins on the ESP8266. The ESP32 has many more interesting pins exposed for the user.
Also added is a way to backup and restore setting to the eeprom so it's easier to swap CPU boards over on your tracker and keep all the settings. Again not new code but lifted from my irrigation controller.
GPSSo by popular demand, how do you lookup the GPS to the tracker ?
TX on GPS to RX on ESP32 (purple in this pic). Power the GPS from 3.3 Volts on the ESP32 board. Set the WEB UI to option 1 - Use GPS and press set... then save this to eeprom, then most important REBOOT. When the GPS locks this should show on the WEB interface alone with the number of satellites etc. This applies to both ESP32 and ESP8266 versions.
Remember: You will need to disconnect the GPS TX line (goes to ESP RX pin) to be able to update the sketch via USB. If your using OTA WEB update then you can leave it connected while doing an update.
Update March 2023 - Data LoggingJust for fun I've added some data logging for the last 24 hrs so you can plot check the command and target angles as well as the RSSI, temperature and pressure.
More than part way on several other things such as the auto reboot, emailed alarms, power saving and static IP address.
I have cleaned up the web GUI a bit so it loads way faster on marginal WiFi.
Comments