I just finished writing about my ESP8266+ESPHome Ping Tester – and realised that, as I was only using 43% of RAM and 48% of FLASH memory – it would be a crime to stop there – hence this follow-up article.
The device reads pings from the network and thanks to an ESPHome WebUI (i.e. web page on the device, accessible via a browser) it is easy to select both internal and external IP addresses to ping. You get visible ping time, IP adress being pinged and a fail-count which again can be reset at any time in the WebUI.
And all of this from a device that costs less than many ESP32s, even including the WS2812 LED and standard I2c SSD1306 128*64 OLED display. I was expecting some review items to arrive about now but when they didn’t turn up I started to wonder if this device could be improved in the meantime.
This board differs from the original in that I used a board that came complete with display and for the ws2812 RGB LED I simply cut a single section from a spare ws2812b strip and used nanotape to glue it to the ESP8266. That meant a change of i2c pins from D2 and D2 to D5 and D6 which then meant moving the RGB LED from D6 to D7.
All in, I changed the project – big time. I’ve added a 200-ping history, held in the device (time, IP, ping-time for each ping). The pings are stored in RAM and have taken the used-RAM up from around 43% to 47%, the FLASH usage increase being hardly noticeable at 48% up from just over 47%. I’ve added MQTT and works with any old MQTT broker (Home Assistant for example).
This of course demands a new control on the WebUI to pause queue build-up while it is being read hence a new photo of the WebUI. I figured I’d better leave the original project as-is and put the changes and additions in here. Bear in mind, once set up, this doesn’t need a PC with ESPHome – it runs all by itself.
I’ve no doubt at some point I’ll get the urge to add even more – all that spare memory doing nothing…
Meanwhile – the device is set up to use my (Home Assistant) MQTT broker at 192.168.1.20 and with my login credentials. When you compile this – check out your secrets file in ESPHome (not Home Assistant, the secrets file in the ESPHome folder).
Just as an example, this is my secrets file (details altered to protect the innocent) – I only have one password to serve multiple SSIDs as it’s a large house.
wifi_ssid: "myoffice" wifi_password: "xxxxxxxx" uk_wifi_ssid: "office" uk_wifi_password: "xxxxxxxxy" wifi_ssid2: "black" wifi_ssid3: "mlo" wifi_ssid4: "outside" wifi_ssid5: "wififorus" wifi_ssid6: "petes" wifi_ssid7: "comfast" mqtt_broker: 192.168.1.20 mqtt_username: aaaaa mqtt_password: bbbbbb abrobot_ota_password: ccccccccccccccccc ttgo_ota_password: ccccccccccc car_data_write_key: "blah1231blahblah" car_data_read_key: "moreblah123blahblah"
I just finished the above and realised that it would be great to monitor the internal IP of my router – AND something like Google – so if I have any issues (which I do from time to time), I want to SEE failures from each address – and store WHEN the connection last failed for MQTT access but more importantly for instant gratification on the device display. I ould have complicated the code by adding the ability to check multiple addresses at once – but this is so cheap anyway…
And now, the YAML code for the latest Ping Tester. Depending on your board, the I2c may use different pins to those shown in the code (D5 and D6) – check also the pin for the RGB LED – currently set to D7.
substitutions:
device_name: pinger1
friendly_name: Pinger 1
mqtt_prefix: pinger1
device_version: "0.17"
code_source: "https://tech.scargill.net"
esphome:
name: ${device_name}
friendly_name: ${friendly_name}
libraries:
- ESPping@1.0.5
includes:
- <ESPping.h>
on_boot:
priority: -100
then:
- light.turn_on:
id: rgb_led
red: 100%
green: 40%
blue: 0%
esp8266:
board: nodemcuv2
restore_from_flash: true
logger:
level: DEBUG
api:
reboot_timeout: 0s
time:
- platform: sntp
id: esptime
timezone: Europe/Brussels
update_interval: 2s
mqtt:
broker: !secret mqtt_broker
username: !secret mqtt_username
password: !secret mqtt_password
id: mqtt_client
discovery: false
discover_ip: false
web_server:
version: 3
sorting_groups:
- id: web_group_controls
name: "Controls"
sorting_weight: 10
- id: web_group_sensors
name: "Sensors"
sorting_weight: 20
ota:
- platform: esphome
wifi:
networks:
- ssid: !secret wifi_ssid
password: !secret wifi_password
- ssid: !secret wifi_ssid2
password: !secret wifi_password
- ssid: !secret wifi_ssid3
password: !secret wifi_password
- ssid: !secret wifi_ssid4
password: !secret wifi_password
- ssid: !secret wifi_ssid5
password: !secret wifi_password
power_save_mode: none
post_connect_roaming: true
ap:
ssid: "${device_name} Fallback"
password: "12345678"
captive_portal:
preferences:
flash_write_interval: 10s
# ==================================================
# GLOBALS
# ==================================================
globals:
- id: last_failure_time
type: std::string
restore_value: no
initial_value: '""'
- id: ping_state
type: int
restore_value: no
initial_value: "0"
- id: ping_has_succeeded
type: bool
restore_value: no
initial_value: "false"
- id: ping_ms
type: int
restore_value: no
initial_value: "0"
- id: heartbeat
type: bool
restore_value: no
initial_value: "false"
# Persistent ping target IP
- id: ip1
type: int
restore_value: yes
initial_value: "192"
- id: ip2
type: int
restore_value: yes
initial_value: "168"
- id: ip3
type: int
restore_value: yes
initial_value: "1"
- id: ip4
type: int
restore_value: yes
initial_value: "1"
# Persistent failure counter
- id: failed_ping_count
type: uint32_t
restore_value: yes
initial_value: "0"
# Reset switch state
- id: reset_switch_state
type: bool
initial_value: 'false'
# In-RAM ping history. These arrays are deliberately not restored to flash.
- id: history_sequence
type: uint32_t[200]
restore_value: no
- id: history_ms
type: uint16_t[200]
restore_value: no
- id: history_status
type: uint8_t[200]
restore_value: no
- id: history_ip1
type: uint8_t[200]
restore_value: no
- id: history_ip2
type: uint8_t[200]
restore_value: no
- id: history_ip3
type: uint8_t[200]
restore_value: no
- id: history_ip4
type: uint8_t[200]
restore_value: no
- id: history_index
type: uint8_t
restore_value: no
initial_value: '0'
- id: history_count
type: uint16_t
restore_value: no
initial_value: '0'
- id: history_sequence_number
type: uint32_t
restore_value: no
initial_value: '0'
- id: history_paused
type: bool
restore_value: no
initial_value: 'false'
# ==================================================
# FONT
# ==================================================
font:
- file: "gfonts://Roboto"
id: font_small
size: 12
# ==================================================
# I2C OLED
# ==================================================
# your board may use diffent pins for I2C
i2c:
sda: D5
scl: D6
scan: true # added only for the HW-364A boards
frequency: 100khz # added only for the HW-364A boards
# ==================================================
# OLED DISPLAY
# ==================================================
display:
- platform: ssd1306_i2c
model: "SSD1306 128x64"
address: 0x3C
update_interval: 5s # 2s is minimum but we only need 5s pings..
lambda: |-
// OLED disabled
if (!id(oled_display).state) {
it.fill(COLOR_OFF);
return;
}
// Header
it.print(0, 0, id(font_heading), "${friendly_name} v${device_version}");
it.horizontal_line(0,15,128);
// Heartbeat
if (id(heartbeat)) {
it.print(118, 0, id(font_small), "*");
}
it.printf(127, 14, id(font_small), TextAlign::TOP_RIGHT, "%02d:%02d:%02d",
id(esptime).now().hour,
id(esptime).now().minute,
id(esptime).now().second);
// Ping status
if (id(ping_state) == 0) {
it.print(0, 14, id(font_small), "SETTING UP");
} else if (id(ping_state) == 1) {
it.printf(
0, 14,
id(font_small),
"OK %d ms",
id(ping_ms)
);
} else {
if (id(ping_ms) > 500) {
it.printf(
0, 14,
id(font_small),
"FAILED %d ms",
id(ping_ms)
);
} else {
it.print(0, 14, id(font_small), "** FAILED **");
}
}
// Target IP
it.printf(
0, 26,
id(font_small),
"Ping: %d.%d.%d.%d",
id(ip1),
id(ip2),
id(ip3),
id(ip4)
);
// Persistent failure counter
char fail_text[20];
sprintf(
fail_text,
"Failures: %d",
(int) id(failed_ping_count)
);
int fail_width;
int x_offset;
int baseline;
int height;
id(font_small).measure(
fail_text,
&fail_width,
&x_offset,
&baseline,
&height
);
it.print(
0,
38,
id(font_small),
fail_text
);
if (!id(last_failure_time).empty()) {
it.printf(0, 50, id(font_small), "Last Fail: %s",
id(last_failure_time).c_str());
}
# ==================================================
# RGB LED
# ==================================================
light:
- platform: neopixelbus
id: rgb_led
type: GRB
variant: WS2812
# pin: D6
pin: D7
num_leds: 1
name: "RGB LED"
web_server:
sorting_group_id: web_group_controls
sorting_weight: 100
gamma_correct: 1.0
initial_state:
brightness: 26%
restore_state: all
# ==================================================
# TEXT SENSORS
# ==================================================
text_sensor:
- platform: template
name: "Firmware Version"
web_server:
sorting_group_id: web_group_sensors
sorting_weight: 30
lambda: |-
return {"${device_version}"};
update_interval: 60s
- platform: template
name: "code_source"
web_server:
sorting_group_id: web_group_sensors
sorting_weight: 40
lambda: |-
return {"${code_source}"};
update_interval: 60s
# ==================================================
# PING TARGET DROPDOWN
# ==================================================
select:
- platform: template
name: "Ping Target"
id: ping_target
web_server:
sorting_group_id: web_group_controls
sorting_weight: 10
optimistic: true
restore_value: true
initial_option: "Manual / Custom"
options:
- "Manual / Custom"
- "Google DNS - 8.8.8.8"
- "Google DNS - 8.8.4.4"
- "Cloudflare - 1.1.1.1"
- "Cloudflare - 1.0.0.1"
- "Quad9 - 9.9.9.9"
- "Quad9 - 149.112.112.112"
- "OpenDNS - 208.67.222.222"
- "OpenDNS - 208.67.220.220"
- "AdGuard - 94.140.14.14"
- "AdGuard - 94.140.15.15"
- "Router - 192.168.1.1"
- "Router - 192.168.0.1"
- "Router - 192.168.1.254"
- "Router - 192.168.0.254"
- "Local - 10.0.0.1"
- "Local - 10.10.0.1"
- "Local - 10.10.10.1"
- "Local - 172.16.0.1"
- "Local - 172.16.1.1"
on_value:
then:
- lambda: |-
if (x == "Google DNS - 8.8.8.8") {
id(ip1) = 8;
id(ip2) = 8;
id(ip3) = 8;
id(ip4) = 8;
} else if (x == "Google DNS - 8.8.4.4") {
id(ip1) = 8;
id(ip2) = 8;
id(ip3) = 4;
id(ip4) = 4;
} else if (x == "Cloudflare - 1.1.1.1") {
id(ip1) = 1;
id(ip2) = 1;
id(ip3) = 1;
id(ip4) = 1;
} else if (x == "Cloudflare - 1.0.0.1") {
id(ip1) = 1;
id(ip2) = 0;
id(ip3) = 0;
id(ip4) = 1;
} else if (x == "Quad9 - 9.9.9.9") {
id(ip1) = 9;
id(ip2) = 9;
id(ip3) = 9;
id(ip4) = 9;
} else if (x == "Quad9 - 149.112.112.112") {
id(ip1) = 149;
id(ip2) = 112;
id(ip3) = 112;
id(ip4) = 112;
} else if (x == "OpenDNS - 208.67.222.222") {
id(ip1) = 208;
id(ip2) = 67;
id(ip3) = 222;
id(ip4) = 222;
} else if (x == "OpenDNS - 208.67.220.220") {
id(ip1) = 208;
id(ip2) = 67;
id(ip3) = 220;
id(ip4) = 220;
} else if (x == "AdGuard - 94.140.14.14") {
id(ip1) = 94;
id(ip2) = 140;
id(ip3) = 14;
id(ip4) = 14;
} else if (x == "AdGuard - 94.140.15.15") {
id(ip1) = 94;
id(ip2) = 140;
id(ip3) = 15;
id(ip4) = 15;
} else if (x == "Router - 192.168.1.1") {
id(ip1) = 192;
id(ip2) = 168;
id(ip3) = 1;
id(ip4) = 1;
} else if (x == "Router - 192.168.0.1") {
id(ip1) = 192;
id(ip2) = 168;
id(ip3) = 0;
id(ip4) = 1;
} else if (x == "Router - 192.168.1.254") {
id(ip1) = 192;
id(ip2) = 168;
id(ip3) = 1;
id(ip4) = 254;
} else if (x == "Router - 192.168.0.254") {
id(ip1) = 192;
id(ip2) = 168;
id(ip3) = 0;
id(ip4) = 254;
} else if (x == "Local - 10.0.0.1") {
id(ip1) = 10;
id(ip2) = 0;
id(ip3) = 0;
id(ip4) = 1;
} else if (x == "Local - 10.10.0.1") {
id(ip1) = 10;
id(ip2) = 10;
id(ip3) = 0;
id(ip4) = 1;
} else if (x == "Local - 10.10.10.1") {
id(ip1) = 10;
id(ip2) = 10;
id(ip3) = 10;
id(ip4) = 1;
} else if (x == "Local - 172.16.0.1") {
id(ip1) = 172;
id(ip2) = 16;
id(ip3) = 0;
id(ip4) = 1;
} else if (x == "Local - 172.16.1.1") {
id(ip1) = 172;
id(ip2) = 16;
id(ip3) = 1;
id(ip4) = 1;
}
- component.update: ping_ip1
- component.update: ping_ip2
- component.update: ping_ip3
- component.update: ping_ip4
# ==================================================
# PING IP ADDRESS
# ==================================================
number:
- platform: template
name: "Ping IP 1"
id: ping_ip1
web_server:
sorting_group_id: web_group_controls
sorting_weight: 20
min_value: 0
max_value: 255
step: 1
mode: box
lambda: |-
return id(ip1);
set_action:
- lambda: |-
id(ip1) = (int)x;
- select.set:
id: ping_target
option: "Manual / Custom"
- platform: template
name: "Ping IP 2"
id: ping_ip2
web_server:
sorting_group_id: web_group_controls
sorting_weight: 30
min_value: 0
max_value: 255
step: 1
mode: box
lambda: |-
return id(ip2);
set_action:
- lambda: |-
id(ip2) = (int)x;
- select.set:
id: ping_target
option: "Manual / Custom"
- platform: template
name: "Ping IP 3"
id: ping_ip3
web_server:
sorting_group_id: web_group_controls
sorting_weight: 40
min_value: 0
max_value: 255
step: 1
mode: box
lambda: |-
return id(ip3);
set_action:
- lambda: |-
id(ip3) = (int)x;
- select.set:
id: ping_target
option: "Manual / Custom"
- platform: template
name: "Ping IP 4"
id: ping_ip4
web_server:
sorting_group_id: web_group_controls
sorting_weight: 50
min_value: 0
max_value: 255
step: 1
mode: box
lambda: |-
return id(ip4);
set_action:
- lambda: |-
id(ip4) = (int)x;
- select.set:
id: ping_target
option: "Manual / Custom"
# ==================================================
# PING FAILURE COUNTER SENSOR
# ==================================================
sensor:
- platform: template
name: "Ping Failures"
id: failed_ping_counter_sensor
web_server:
sorting_group_id: web_group_sensors
sorting_weight: 10
accuracy_decimals: 0
update_interval: 5s
lambda: |-
return (float) id(failed_ping_count);
- platform: template
name: "Ping History Length"
id: ping_history_length_sensor
web_server:
sorting_group_id: web_group_sensors
sorting_weight: 20
accuracy_decimals: 0
update_interval: 5s
lambda: |-
return (float) id(history_count);
# ==================================================
# OLED ON/OFF SWITCH
# ==================================================
switch:
- platform: template
name: "OLED Display"
id: oled_display
web_server:
sorting_group_id: web_group_controls
sorting_weight: 90
restore_mode: RESTORE_DEFAULT_ON
optimistic: true
turn_on_action:
- lambda: |-
// OLED enabled
turn_off_action:
- lambda: |-
// OLED disabled
- platform: template
name: "Reset Ping Counter"
icon: "mdi:gesture-tap"
web_server:
sorting_group_id: web_group_controls
sorting_weight: 80
id: this_dummy_switch_id
lambda: |-
return id(reset_switch_state);
turn_on_action:
- globals.set:
id: reset_switch_state
value: 'true'
- lambda: |-
id(failed_ping_count) = 0;
- component.update: failed_ping_counter_sensor
- delay: 150ms
- switch.turn_off: this_dummy_switch_id
turn_off_action:
- globals.set:
id: reset_switch_state
value: 'false'
- platform: template
name: "Clear Ping History"
icon: "mdi:delete-sweep"
web_server:
sorting_group_id: web_group_controls
sorting_weight: 70
id: clear_ping_history
optimistic: true
turn_on_action:
- lambda: |-
id(history_index) = 0;
id(history_count) = 0;
id(history_sequence_number) = 0;
char topic[24];
for (int n = 0; n < 200; n++) {
snprintf(topic, sizeof(topic), "${mqtt_prefix}/%d", n + 1);
id(mqtt_client).publish(topic, "", 0, 0, true);
}
id(mqtt_client).publish("${mqtt_prefix}/count", "0", 1, 0, true);
if (id(history_paused)) {
id(mqtt_client).publish("${mqtt_prefix}/status", "PAUSED", 6, 0, true);
} else {
id(mqtt_client).publish("${mqtt_prefix}/status", "RUNNING", 7, 0, true);
}
- component.update: ping_history_length_sensor
- delay: 150ms
- switch.turn_off: clear_ping_history
turn_off_action:
- lambda: |-
// Momentary switch.
- platform: template
name: "Pause Ping History"
id: pause_ping_history
web_server:
sorting_group_id: web_group_controls
sorting_weight: 60
optimistic: true
turn_on_action:
- lambda: |-
id(history_paused) = true;
char topic[24];
char payload[96];
// Publish a retained snapshot: newest entry is ${mqtt_prefix}/1.
for (int n = 0; n < id(history_count); n++) {
int idx = ((int)id(history_index) + 200 - 1 - n) % 200;
snprintf(topic, sizeof(topic), "${mqtt_prefix}/%d", n + 1);
if (id(history_status)[idx] == 1) {
snprintf(payload, sizeof(payload),
"#%lu | %d.%d.%d.%d | %d ms | OK",
(unsigned long)id(history_sequence)[idx],
(int)id(history_ip1)[idx], (int)id(history_ip2)[idx],
(int)id(history_ip3)[idx], (int)id(history_ip4)[idx],
(int)id(history_ms)[idx]);
} else if (id(history_status)[idx] == 2) {
snprintf(payload, sizeof(payload),
"#%lu | %d.%d.%d.%d | %d ms | SLOW",
(unsigned long)id(history_sequence)[idx],
(int)id(history_ip1)[idx], (int)id(history_ip2)[idx],
(int)id(history_ip3)[idx], (int)id(history_ip4)[idx],
(int)id(history_ms)[idx]);
} else {
snprintf(payload, sizeof(payload),
"#%lu | %d.%d.%d.%d | FAILED",
(unsigned long)id(history_sequence)[idx],
(int)id(history_ip1)[idx], (int)id(history_ip2)[idx],
(int)id(history_ip3)[idx], (int)id(history_ip4)[idx]);
}
id(mqtt_client).publish(topic, payload, strlen(payload), 0, true);
}
// Clear any old retained entries beyond the current history length.
for (int n = id(history_count); n < 200; n++) {
snprintf(topic, sizeof(topic), "${mqtt_prefix}/%d", n + 1);
id(mqtt_client).publish(topic, "", 0, 0, true);
}
snprintf(payload, sizeof(payload), "%u", id(history_count));
id(mqtt_client).publish("${mqtt_prefix}/count", payload, strlen(payload), 0, true);
id(mqtt_client).publish("${mqtt_prefix}/status", "PAUSED", 6, 0, true);
turn_off_action:
- lambda: |-
id(history_paused) = false;
char topic[24];
char payload[96];
// Publish the current history snapshot and resume live updates.
for (int n = 0; n < id(history_count); n++) {
int idx = ((int)id(history_index) + 200 - 1 - n) % 200;
snprintf(topic, sizeof(topic), "${mqtt_prefix}/%d", n + 1);
if (id(history_status)[idx] == 1) {
snprintf(payload, sizeof(payload),
"#%lu | %d.%d.%d.%d | %d ms | OK",
(unsigned long)id(history_sequence)[idx],
(int)id(history_ip1)[idx], (int)id(history_ip2)[idx],
(int)id(history_ip3)[idx], (int)id(history_ip4)[idx],
(int)id(history_ms)[idx]);
} else if (id(history_status)[idx] == 2) {
snprintf(payload, sizeof(payload),
"#%lu | %d.%d.%d.%d | %d ms | SLOW",
(unsigned long)id(history_sequence)[idx],
(int)id(history_ip1)[idx], (int)id(history_ip2)[idx],
(int)id(history_ip3)[idx], (int)id(history_ip4)[idx],
(int)id(history_ms)[idx]);
} else {
snprintf(payload, sizeof(payload),
"#%lu | %d.%d.%d.%d | FAILED",
(unsigned long)id(history_sequence)[idx],
(int)id(history_ip1)[idx], (int)id(history_ip2)[idx],
(int)id(history_ip3)[idx], (int)id(history_ip4)[idx]);
}
id(mqtt_client).publish(topic, payload, strlen(payload), 0, true);
}
for (int n = id(history_count); n < 200; n++) {
snprintf(topic, sizeof(topic), "${mqtt_prefix}/%d", n + 1);
id(mqtt_client).publish(topic, "", 0, 0, true);
}
snprintf(payload, sizeof(payload), "%u", id(history_count));
id(mqtt_client).publish("${mqtt_prefix}/count", payload, strlen(payload), 0, true);
id(mqtt_client).publish("${mqtt_prefix}/status", "RUNNING", 7, 0, true);
# ==================================================
# PING TEST
# ==================================================
interval:
- interval: 5s
then:
- lambda: |-
id(heartbeat) = !id(heartbeat);
IPAddress target(
id(ip1),
id(ip2),
id(ip3),
id(ip4)
);
// History status: 1=OK, 2=SLOW, 3=FAILED.
uint8_t history_result = 3;
if (Ping.ping(target, 3)) {
id(ping_ms) = (int) Ping.averageTime();
// Ignore slow-ping failures during the first 30 seconds after boot.
if (millis() >= 30000 && id(ping_ms) > 500) {
id(failed_ping_count)++;
auto now = id(esptime).now();
if (now.is_valid()) {
char buf[17];
snprintf(buf, sizeof(buf), "%02d/%02d %02d:%02d",
now.day_of_month,
now.month,
now.hour,
now.minute);
id(last_failure_time) = buf;
}
}
if (id(ping_ms) > 500) {
id(ping_state) = 2;
history_result = 2;
} else {
id(ping_state) = 1;
history_result = 1;
}
id(ping_has_succeeded) = true;
auto call = id(rgb_led).turn_on();
bool local_target =
(id(ip1) == 192 &&
id(ip2) == 168);
if (local_target) {
if (id(ping_ms) < 20) {
call.set_rgb(0.0, 1.0, 0.0);
} else if (id(ping_ms) < 50) {
call.set_rgb(0.5, 1.0, 0.0);
} else if (id(ping_ms) <= 100) {
call.set_rgb(1.0, 1.0, 0.0);
} else {
call.set_rgb(1.0, 0.4, 0.0);
}
} else {
if (id(ping_ms) < 75) {
call.set_rgb(0.0, 1.0, 0.0);
} else if (id(ping_ms) < 125) {
call.set_rgb(0.5, 1.0, 0.0);
} else if (id(ping_ms) <= 175) {
call.set_rgb(1.0, 1.0, 0.0);
} else {
call.set_rgb(1.0, 0.4, 0.0);
}
}
call.perform();
} else {
// Complete ping failure.
id(ping_ms) = 0;
// During the first 30 seconds, keep the startup display and LED
// indication instead of showing a temporary ping failure.
if (millis() >= 30000) {
id(ping_state) = 2;
id(failed_ping_count)++;
auto now = id(esptime).now();
if (now.is_valid()) {
char buf[17];
snprintf(buf, sizeof(buf), "%02d/%02d %02d:%02d",
now.day_of_month,
now.month,
now.hour,
now.minute);
id(last_failure_time) = buf;
}
auto call = id(rgb_led).turn_on();
call.set_rgb(1.0, 0.0, 0.0);
call.perform();
} else {
id(ping_state) = 0;
// Leave the startup LED colour unchanged during warm-up.
}
}
// Store this result in the fixed-size RAM ring buffer.
uint8_t h = id(history_index);
id(history_sequence_number)++;
id(history_sequence)[h] = id(history_sequence_number);
id(history_ms)[h] = (id(ping_ms) < 65535) ? id(ping_ms) : 65535;
id(history_status)[h] = history_result;
id(history_ip1)[h] = id(ip1);
id(history_ip2)[h] = id(ip2);
id(history_ip3)[h] = id(ip3);
id(history_ip4)[h] = id(ip4);
id(history_index) = (h + 1) % 200;
if (id(history_count) < 200) {
id(history_count)++;
}
// While running, publish only the newest result and small status values.
// The full 200-entry retained snapshot is published when paused.
if (!id(history_paused)) {
char latest[96];
if (history_result == 1) {
snprintf(latest, sizeof(latest),
"#%lu | %d.%d.%d.%d | %d ms | OK",
(unsigned long)id(history_sequence_number),
id(ip1), id(ip2), id(ip3), id(ip4), id(ping_ms));
} else if (history_result == 2) {
snprintf(latest, sizeof(latest),
"#%lu | %d.%d.%d.%d | %d ms | SLOW",
(unsigned long)id(history_sequence_number),
id(ip1), id(ip2), id(ip3), id(ip4), id(ping_ms));
} else {
snprintf(latest, sizeof(latest),
"#%lu | %d.%d.%d.%d | FAILED",
(unsigned long)id(history_sequence_number),
id(ip1), id(ip2), id(ip3), id(ip4));
}
id(mqtt_client).publish("${mqtt_prefix}/latest", latest, strlen(latest), 0, false);
char count_text[12];
snprintf(count_text, sizeof(count_text), "%u", id(history_count));
id(mqtt_client).publish("${mqtt_prefix}/count", count_text, strlen(count_text), 0, true);
id(mqtt_client).publish("${mqtt_prefix}/status", "RUNNING", 7, 0, true);
}
To get the history info from MQTT – the following subscribes are available:
pinger1/1 pinger1/2 pinger1/3 … pinger1/200
pinger1/latest pinger1/count pinger1/status
For a quick check just subscribe to pinger1/#
Obviously where I refer to “pinger1” – if you make TWO devices – change the substitution section at the top of the code to (for example):
substitutions: device_name: pinger2 friendly_name: Pinger 2 mqtt_prefix: pinger2

