XTM18S wattmeter - need help updating code "IRAM"

Hello,
Still programming noob here, so sorry for the simple question.

I am trying to use a XTM18S wattmeter (gives pulses) to get power use via MQTT to Home assistant,

I found some examples online and used the code but they are a few years old and some programming rules seemed to have changed, and I would like to request some help in updating it.
I played around with the suggested changes but I dont know enough to get it to work (im more of a copy/paste frankenstein code kind of guy thats flying blind :slight_smile: )

The code I use (dont worry about the MQTT passwords, gona change that all later)

/**
 * This script is based on these sources:
 *
 * To count pulses using interruptions:  https://github.com/mysensors/MySensors/blob/master/examples/EnergyMeterPulseSensor/EnergyMeterPulseSensor.ino
 * To connect to Wifi and publish MQTT messages:  https://github.com/knolleary/pubsubclient/blob/master/examples/mqtt_esp8266/mqtt_esp8266.ino
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 *
 *******************************
 *
 * DESCRIPTION
 * Use this sensor to measure kWh and Watt of your house meter.
 * You need to set the correct pulsefactor of your meter (pulses per kWh).
 * Reports every SEND_FREQUENCY miliseconds: pulses counting, kWh and Watt to different MQTT topics.
 *
 */

#include <ESP8266WiFi.h>
#include <PubSubClient.h>

// Update these with values suitable for your network.

/************************* WiFi Access Point *********************************/

const char* ssid = "SSID";
const char* password = "PASSWORD";

/**************************** MQTT Broker ************************************/

const char* mqtt_server = "192.168.11.99"; // example: "192.168.0.8"
const char* mqtt_username = "arduino";
const char* mqtt_password = "mQTT5-ardui";
const char* mqtt_topic_watt = "ESP-energy-01/watt";
const char* mqtt_topic_kwh = "ESP-energy-01/kwh";
const char* mqtt_topic_pulse = "ESP-energy-01/pulse";

// Define the ISR function
void ICACHE_RAM_ATTR ISRoutine() {
    // Your interrupt handling code here
}

#define DIGITAL_INPUT_SENSOR 12 // The digital input you attached S0+ D6 in Wemos D1 mini
#define PULSE_FACTOR 2000       // Nummber of pulses per kWh of your meeter
#define MAX_WATT 10000          // Max watt value to report. This filters outliers.

unsigned long SEND_FREQUENCY = 20000; // Minimum time between send (in milliseconds)
double ppwh = ((double)PULSE_FACTOR)/1000; // Pulses per watt hour
volatile unsigned long pulseCount = 0;
volatile unsigned long lastBlink = 0;
volatile unsigned long watt = 0;
unsigned long oldWatt = 0;
double oldKwh;
unsigned long lastSend;

WiFiClient espClient;
PubSubClient client(espClient);

long lastMsg = 0;
char msg[50];
char wattString[6];
char kwhString[6];
char pulseCountString[6];

void setup_wifi() {
  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

// Setup a MQTT subscription
void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect("ESP8266Client", mqtt_username, mqtt_password)) {
      Serial.println("connected");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

void setup()
{
  Serial.begin(115200);
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
  // Use the internal pullup to be able to hook up this sketch directly to an energy meter with S0 output
  // If no pullup is used, the reported usage will be too high because of the floating pin
  pinMode(DIGITAL_INPUT_SENSOR,INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(DIGITAL_INPUT_SENSOR), onPulse, RISING);
  lastSend=millis();
}

void loop()
{
    if (!client.connected()) {
        reconnect();
    }
    client.loop();

    unsigned long now = millis();
    // Only send values at a maximum frequency
    bool sendTime = now - lastSend > SEND_FREQUENCY;
    if (sendTime) {
        // New watt value has been calculated
        if (watt != oldWatt) {
            // Check that we dont get unresonable large watt value.
            // could hapen when long wraps or false interrupt triggered
            if (watt<((unsigned long)MAX_WATT)) {
                // convert to a string with 2 digits before the comma and 2 digits for precision
                dtostrf(watt, 4, 1, wattString);
                client.publish(mqtt_topic_watt,wattString);  // Publish watt to MQTT topic
            }
            Serial.print("Watt:");
            Serial.println(wattString);
            oldWatt = watt;
            dtostrf(pulseCount, 4, 1, pulseCountString); // To Do: convert int to str, but not like this
            client.publish(mqtt_topic_pulse,pulseCountString);  // Publish pulses to MQTT topic
            double kwh = ((double)pulseCount/((double)PULSE_FACTOR));
            // convert to a string with 2 digits before the comma and 2 digits for precision
            dtostrf(kwh, 2, 2, kwhString);
            client.publish(mqtt_topic_kwh,kwhString);  // Publish kwh to MQTT topic
            oldKwh = kwh;
            lastSend = now;  // once every thing is published we update the send time
        }
    }

}

void onPulse()
{
    unsigned long newBlink = micros();
    unsigned long interval = newBlink-lastBlink;
    if (interval<10000L) { // Sometimes we get interrupt on RISING
            return;
    }
    watt = (3600000000.0 /interval) / ppwh;
    lastBlink = newBlink;
    pulseCount++;
}

This compiles fine, but I get the following error in serial monitor;

...

WiFi connected

IP address:

192.168.11.199

ISR not in IRAM!

User exception (panic/abort/assert)

--------------- CUT HERE FOR EXCEPTION DECODER ---------------

Abort called

stack>>>

When I do some googling I found this information (seems to have been a change since core 2.5.0

I don’t want to re-open a solved issue, but I discovered you just need to add ICACHE_RAM_ATTR in front of your ISR function to solve this issue. It tells the compiler to keep the ISR function in memory and is required for ESP core 2.5.1 and later.

But for the life of me I cant understand exactly what I have to change in my code to update it to this new way :frowning:

Any chanse any codeninja can quickly help me on my way here ??

thank you in advance,

Hans

I know of IRAM_ATTR, but first try what was suggested in the other post. Just add that in front of the ISR. I think it goes between void and onPulse

Delete this

and do this.

void IRAM_ATTR onPulse()

This is the new way to specify locating the interrupt handler in IRAM.
Others will probably point out you are doing way to much in an interrupt routine

1 Like

Thanks alot, I will try it that way,

Yeah I found this complete code online (posted few years ago) my programming skills are just "copy / paste" :slight_smile: but im learning every day, maybey someday I can make this from scratch and use all the good rules (like not do to much in the interrupt routine) :slight_smile:
thank you,

I will let you know next week if it worked,

Hello, if I only change what you told me, I get a compile error ;

expected initializer before "unsigned long now = millis();" (line 133 )

but im to stupid with coding to know whats wrong, I tried adding { and } but than I get the compile error higher up in my code also with an "unsigned" , I gues those Unsigned things are changed now also ??

Any more help is afcourse appreciated,

sorry for beeing such a noob,

Hans

Without seeing ALL the code (in code tags) AND the FULL error log (in code tags) it is impossible to determine the problem.

Think I have it working,

Instead of putting the new code where the old code was, I needed to put this code just before void setup()

Thanks alot,