Latest on ESP32

The ESP8266 consumes all of my time. I expect to release what I have for the ESP32 this fall/winter for those that want to tinker. Getting it to the stability and compliance of the current unit is a huge expensive effort and I don’t see myself able to do that and support 10,000 current users at the same time.

Understandable. Reason I was asking was I’m preparing to move and the buyer of my place requested the IotaWatt’s to go with it. Figured why not since I can just replace them and wanted to see if possible the ESP32 would be coming out as a nice upgrade, but the older ESP8266 ones are also acceptable.

What would be the most time consuming? Is it more hardware work or more software one to be performed yet?

Overeasy, any plans to publish your esp32 firmware any time soon? I am really curious on your new sampling routine on the ESP32. SPI and/or bare metal work differently on the ESP32. I am starting a new project with a MCP3208 and using the ESP_IDF or android libraries I am getting sampling rates far below yours.

Are you talking about sampling AC power or just driving the MCP3208?

Mostly driving the MCP3208. Your approach, it seems to me, is to partly use esp8266 SPI and for the most part bitbang and write/read SPI registers.
Obviously this is different on a ESP32

You use standard SPI here:

  rawV = readADC(Vchan) - offsetV;                    // Prime the pump

You loop through samples with reading registers

// hardware send 5 bit start + sgl/diff + port_addr
        SPI1U1 = (SPI1U1 & mask) | dataMask;               // Set number of bits 
        SPI1W0 = (0x18 | Iport) << 3;                      // Data left aligned in low byte 
        SPI1CMD |= SPIBUSY;                                // Start the SPI clock  


// extract the rawV from the SPI hardware buffer and adjust with offset. 
        rawI = (word(*fifoPtr8 & 0x01, *(fifoPtr8+1)) << 3) + (*(fifoPtr8+2) >> 5) - offsetI;

All very nicely programmed. ESP32 documentation is very vague at times. In some way it claims that the ESP32 has so much more power and memory you don’t need to employ methods like yours. But with using ESP32 SPI Master driver I get much lower rates than your ESP8266 method - on a ESP8266 of course.

Just running the SPI/ADC is not that complicated. It’s simpler than the ESP8266.

I run the SPI at 2MHz:

vspi->beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE0));

It’s been a couple of years since I coded this up, so my memory is a little off. I use the standard digitalWrite to lower and raise the CS pin.

For the actual SPI transaction I use a call to a modified lower level spiTransferBits that I renamed spiTransferBitsCB. There are two modifications:

The MCP3208 transaction is broken down into two SPI transactions. The first is the 5 bit write portion with start bit and channel select. The second is the 12 bit result. The reason for this is that when you run the MCP3208 ate 3.3V and 2MHz, the sample and hold charge time can be too short to fully charge the ADC sample capacitor. The cap charge time is increased by the inter-transaction delay time.

The second modification is to include the ability to specify a callback function to be invoked when the second SPI transaction initiates. This is used to do housekeeping and other sampling chores during the 6us data transfer from the ADC. It’s not necessary to use this callback to get the high transaction rate. I implement it as a lambda expression in the code as in:

                // Sample Current (I) channel

            digitalWrite(context.Ics, LOW);
           spiTransferBitsCB(spi, context.Iport, &spiOut, [ ](void* ptr){

                    // This code is a callback from the low level SPI
                    // while the SPI is reading the ADC.
                    // Here we do whatever housekeeping possible asynchronously.
                    // It should complete before the SPI transfer finishes. 
                    .  // Some housekeeping code

            }, (void*)&context);

            digitalWrite(context.Ics, HIGH);            
            context.newI = (spiOut & 4095) - job->offsetI;

The modified spiTransferBits code follows:

//       spiTransferBitsCB()
//      Reworked spiTransferBit from ESP32/Arduino core.
//      Special purpose to perform MCP3208 transaction at high speed with 2MHz spi.
//      When running at 2MHz 750ns ADC sample time is inadequate and produces lower quality results of +/- 3 LSB.
//      By splitting into two transactions, the sample time defined by the rise of the 5th clock and fall of the 6th clock
//      is greatly increased to about 2ms, producing better results.
//      This code also allows for a callback during the second 14 bit transfer (7ms), which the sampler
//      uses to record results, do housekeeping and loop control.
//      The result is an effective ADC sample rate of about 80K SPS.
//      Returns boolean noWait to indicate if no wait for spi completion was required after return from the
//      callback, which means the callback did not complete in the alloted time.  This is used from time-to-time
//      as an aid to tuning the loop to balance the two spi calls and minimize sampling induced phase-shift.

#include "esp_attr.h"

#include "esp32-hal-spi.h"
#include "esp32-hal.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "rom/ets_sys.h"
#include "esp_attr.h"
#include "esp_intr.h"
#include "rom/gpio.h"
#include "soc/spi_reg.h"
#include "soc/spi_struct.h"
#include "soc/io_mux_reg.h"
#include "soc/gpio_sig_map.h"
#include "soc/dport_reg.h"
#include "soc/rtc.h"

struct spi_struct_t {
    spi_dev_t * dev;
    xSemaphoreHandle lock;
    uint8_t num;

bool IRAM_ATTR spiTransferBitsCB(spi_t * spi, uint32_t port, uint32_t * out, void (*cb)(void*), void* cbParm)
    // hard coded for 19 bits split into 5 and 14 bit transactions.
    // ADC sample capacitor charges between rise of 5th clock and fall of 6th clock,
    // so splitting increases the sample time which produces more reliable results at 2MHz clk.

    bool noWait = false;

    // Setup 5 bit command transfer.
    // port is combined with start and sgl/diff bits and shifted to 
    // left align in the little-endian LSB which stores as the big-endian MSB for spi.
    spi->dev->data_buf[0] = (0b11000 | port) << 3;
    spi->dev->mosi_dlen.usr_mosi_dbitlen = (5 - 1);
    spi->dev->miso_dlen.usr_miso_dbitlen = (5 - 1);
    spi->dev->cmd.usr = 1;

    // Now start the 14 bit result transfer

    spi->dev->mosi_dlen.usr_mosi_dbitlen = (14 - 1);
    spi->dev->miso_dlen.usr_miso_dbitlen = (14 - 1);
    spi->dev->data_buf[0] = 0;
    spi->dev->cmd.usr = 1;

    // Initiate the call back if requested

    if(cb) (*cb)(cbParm);

    // Now wait for completion (or not)

    else {
        noWait = true;
    // The result is in the high order 14 bits of the adc buffer in big-endian format.
    // This code extracts those bits and reformats to little-endian.

    uint32_t _out = spi->dev->data_buf[0];
    *out = (_out >> 10) | ((_out & 0xff) << 6);
    return noWait;

The IoTaWatt sampler that is using this code runs in a mostly dedicated processor. This is accomplished during startup in the the arduino environment by switching the arduino task to core 0, and later starting a FreeRTOS sampler task on core 1. All other tasks are assigned to core 0. You may not need to do that depending on your sampling requirements, but here’s how I do it.

void setup(){

        // Switch arduino framework to core 0

    if(xPortGetCoreID() == 1){
        xTaskCreateUniversal(loopTask, "looptask0", 6000, NULL, X_PRI_LOOP, &loopTaskHandle, 0);

Beyond that, the sampler task on core 1 receives work-order messages using XqueueReceive and sends the results to a post processor on core 0. Work-orders typically specify taking a set of samples of voltage and current for one AC cycle.

So, I’m looking at building my own iotawatt. I have 2 panels, one of which with 26 breakers. I was thinking of building 3 8266-based boards to handle all the breakers, but now I’m debating whether or not I should build those boards, or wait for this ESP32-based version to be available? Seems like it would be nice to have more power from the ESP-32, as well as having the option for an expansion board for the panel with 26 breakers. Which route would you go if you were in my position?

A bird in the hand….

Good to see the ESP32 plans (I’m late to the party!).

Any news on when the version will be ready for people to assemble themselves and help test? Very interesting progression of iotawatt.

Given the global shortages of components, is there any information available upon what specific ESP32 parts you’ve used? Just thinking if for those wanting to tinker in retirement, apart from making sure to source the correct parts, it might also be worth ordering parts upfront now… and hopefully avoid any delays, for when you release if come later this year.


Given the mention of Ethernet and ESP32, this might be of interest to some…. and also supports POE.

TTGO T- Internet-POE ESP32 Ethernet adapters and download, extension kit

1 Like

A post was split to a new topic: Three-Phase Three-Wire

@overeasy ,

Thought this might be of interest to you, give some ideas…

ESP32 based monitoring board system, that expands up to 42 circuits able to be connected and monitored. No SD card unfortunately, otherwise being able to purchase a license for your IoTAWatt on ESP32 and then install upon such a kit would be great. Hardware certification and distribution / support issues get offloaded also…

Personally I don’t really like the idea of stacking them. I looked at that product before I decided on iotawatt and chose iotawatt mainly for the form factor, and being open source but both of these products are. Stacking them would require me to print a new enclosure everytime I expand the system and stacking it further out from the wall if wall mounted.

I think it would be cool to have the option to buy/build pluggable expansions. For example, if the main unit had a few 4 pin jst connections on the side of it that all went to one of the i2c ports on the ESP32 (something the esp8266 lacks). Then expansion modules could be smaller and cheaper because they just add more ports that are all read over i2c on the main unit. Similar to how the Arduino Port Expander works with esphome… Arduino Port Expander — ESPHome

What I do like about that CircuitSetup product is having the ESP on a pin header so it can be easily swapped out. Not only does it make it easy to change the ESP if it dies (and they do) but it also makes it easier to devlop and test features by just swapping out ESP modules without having to overwrite the stable one. Like, why use a development module in the main product but solder it to the board? Kinda defeats the purpose and a really bad decision in my opinion. That alone almost made me go with that product instead of this one. However, I’m good at soldering so if my ESP does die for some reason then I can replace it with a pin header myself. It’s not something I’d bother with though unless there was an urgent need for it.

While I do have a degree in EE, I don’t play one at work (I use the other half of my degree, CS). I have learned from my professional EE friends that solder actually has much higher reliability than a socket connection. So while a socket makes it easier to replace the part, the socket also makes it more likely that the part will fail.

I have a lot of esp8266 based devices at home. The only failure I have encountered occurred when I used a 5V programmer. Your experience is probably different from mine.

If one of mine died, I would probably just buy a new one and put the old one on the pile of stuff I need to fix when I have enough free time (probably after I am dead :wink:).

A socket failure would only ever be a concern for someone that’s constantly removing it and that type of person probably knows what to do if it happens or at least accepts the risk in doing so. A failure of the esp pin header is way less likely than a failure of the usb port, barrel connector, or one of the 14 3.5mm audio jacks. Besides, I’d rather replace an $8 esp32 than a $150 iotawatt.

Also I think the majority of people buying iotawatt is because it’s open source and they want the freedom to tinker and customize. Which we can and I’m not complaining at all. Just saying it could be better. And those who don’t care, well, they’ll never open it up anyways and it won’t make any difference to them. It’ll still function the same.

Btw, I also have a lot of esp32 and esp8266 based devices at home. Also, arduino, nrf24, zigbee, xbee, about a dozen custom quad copters, and several other projects, almost all of which I built myself. I have stuff still sitting on breadboards that have been running for more than 10 years. I’ve never once experienced a pin header failure that wasn’t caused by something stupid i did.

The reason I use the Devkit rather than discrete components is both practical and economic. The Devkits cost substantially less than the discrete components and the whole unit comes assembled and tested. There is an additional cost to wave solder it on, but the aggregate cost of SMT assembly of all the individual components is nearly the same.

I can count on one hand the number of units I’ve replaced for Devkit failure on any kind. It’s a simple matter to install sockets but the enclosure no longer fits, and there is an additional cost to that as well. You can easily cut out the Devkit of a production unit and add sockets, but as you point out, virtually all users are disinterested in having a socket.

Manufacturing with sockets does allow a reduced price point and makes it more of a DIY device. Flashing and testing is eliminated. So that may be more of the reason the above ESP32 product breaks that out.

My ESP32 prototype was designed to fit in the current ESP8266 enclosure. It does allow for an expansion board that adds another 14 channels. The expansion board is connected with a FFC.

The idea was that a spacer could be added between the top and bottom of the enclosure to get a stacked 28 channel unit. But the two boards could optionally be placed in individual enclosures and connected by a longer FFC. I have it working with an 18" FFC, but haven’t determined how long a cable is possible.

I currently intend to finish this as a DIY project and not offer finished production units. I’m tired. I’d like to get back to programming for the fun of it rather than running a business.


Oh, man, do I hear that! I got into computers, cold, in '81 with an Apple ][ and went “pro” in '85 thus nearly destroying my hobby. I’ve had a woodshop since '09 and many have asked that I make things for them and, recalling the computer experience, I always decline. Good on you, @overeasy, for getting back to what you love the most. :clap:

Regarding sockets, this brings up some of the points I did:

Bottom line, expensive high quality sockets are fine, cheap sockets not so much. For this topic, it probably matters as much as “how many angels can dance of the head of a pin”