NodeMCU WiFi Toilet Paper Sheet Counter
Keep track of use and abuse.
Whose butt does the most damage
to the planet and budget?!


NodeMCU
Hall Sensor Module
Neodymium Magnets (2)
16X2 LCD Display
Momentary Buttons (2)
10K Resistor (2)
1K Resistor (1)
10K Potentiometer
USB Female Module
5V 2A Power Supply

ThingSpeak
https://thingspeak.com/channels/1631806

Sheet count maps to average per rotation per total use. The formula is determined by average count of (4) sheets per complete exterior rotation and (1) sheet per complete interior rotation.

Primary error in accuracy will be found in the round() function for display purposes. If you were to remove the display or isolate the values from each other then you would have results different than displayed. If you display decimal values then it becomes untidy. ~(°-°)~

(322.021) Code updated to repair missing reset zeroing.



// toilet paper sheet counter
// vije miller

#include <LiquidCrystal.h>
#include <ESP8266WiFi.h>
#include "MapFloat.h"

// display
LiquidCrystal lcd(D6, D5, D4, D3, D2, D1);

// sensor
const int hall = D7;
int hs, hn;

// button
const int b_left = D0;
const int b_right = D8;
int bl, br;

// states
int new_roll, total_sheets, sheets_left;
int pull_now, last_pull, pull_total;
long pull_value, passed;
long hold = 300000; // 5 minutes

// wifi
const char* ssid = "yourssid";
const char* password = "yourpass";
WiFiServer server(80);

// thingspeak
char thingSpeakAddress[] = "api.thingspeak.com";
String APIKey = "yourapikey"; // API
const int updateThingSpeakInterval = 10 * 1000;
long lastConnectionTime = 0;
boolean lastConnected = false;
WiFiClient client;

void setup() {
Serial.begin(115200);
lcd.begin(16, 2);
lcd.setCursor(0, 0);
lcd.print("Toilet Paper");
lcd.setCursor(0, 1);
lcd.print("Sheet Counter");
delay(1500);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Connecting...");
while (WiFi.status() != WL_CONNECTED) {
WiFi.begin(ssid, password);
delay(10000);
}
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Connected");
server.begin();
delay(1000);
lcd.clear();
pinMode(b_left, INPUT);
pinMode(b_right, INPUT);
pinMode(hall, INPUT);
}

void loop() {
// new roll setting
if (new_roll == 0) {
select_roll();
}
// read roll
hs = digitalRead(hall);
if (hs == LOW) {
// to isolate requests
if (hn == 0) {
// spiral maths
pull_value = mapFloat(pull_total, 0, total_sheets, 2.25, 0.5);
pull_value = round(pull_value); // this ruins it
pull_now = pull_now + pull_value;
pull_total = pull_total + pull_value;
sheets_left = total_sheets - pull_total;
update_display();
// states
passed = 0;
hn = 1;
}
} else {
hn = 0;
}
// dont count time if inactive
if (pull_now != 0) {
passed++;
Serial.println(passed);
}
// send and reset
if (passed > hold) {
net_send();
last_pull = pull_now;
pull_now = 0;
passed = 0;
lcd.setCursor(5, 0);
lcd.print(" ");
lcd.setCursor(5, 1);
lcd.print(" ");
update_display();
}
// end of roll
if (sheets_left < 1) {
new_roll = 0;
}
// reset roll button
br = digitalRead(b_right);
if (br == HIGH) {
new_roll = 0;
delay(700);
}
}

void select_roll() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Select Sheets");
total_sheets = 200;
while (digitalRead(b_right) == LOW) {
lcd.setCursor(0, 1);
lcd.print(total_sheets);
bl = digitalRead(b_left);
if (bl == HIGH) {
total_sheets = total_sheets + 5;
delay(100);
}
yield(); // esp fix
}
last_pull = 0;
pull_total = 0;
pull_now = 0;
sheets_left = total_sheets;
new_roll = 1;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Last");
lcd.setCursor(5, 0);
lcd.print(last_pull);
lcd.setCursor(10, 0);
lcd.print("(+");
lcd.setCursor(12, 0);
lcd.print(pull_total);
lcd.setCursor(15, 0);
lcd.print(")");
lcd.setCursor(0, 1);
lcd.print("Now");
lcd.setCursor(5, 1);
lcd.print(pull_now);
lcd.setCursor(10, 1);
lcd.print("(-");
lcd.setCursor(12, 1);
lcd.print(sheets_left);
lcd.setCursor(15, 1);
lcd.print(")");
delay(500);
}

void update_display() {
lcd.setCursor(5, 0);
lcd.print(last_pull);
lcd.setCursor(12, 0);
lcd.print(pull_total);
lcd.setCursor(5, 1);
lcd.print(pull_now);
lcd.setCursor(12, 1);
lcd.print(sheets_left);
}

void net_send() {
String last_sheet = String(pull_now, DEC);
String last_total = String(pull_total, DEC);
updateThingSpeak("field1=" + last_sheet + "&field2=" + last_total);
delay(3000);
}

void updateThingSpeak(String tsData) {
if (client.connect(thingSpeakAddress, 80)) {
client.print("POST /update HTTP/1.1\n");
client.print("Host: api.thingspeak.com\n");
client.print("Connection: close\n");
client.print("X-THINGSPEAKAPIKEY: " + APIKey + "\n");
client.print("Content-Type: application/x-www-form-urlencoded\n");
client.print("Content-Length: ");
client.print(tsData.length());
client.print("\n\n");
client.print(tsData);
}
}


@pkvi
KE=½mv²
10,617 miters
8 miters today
195 tenons
Subscribe