#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
/* Open network, no pass, free 4 all. */
const char *ssid = "TIMBANGAN ONLEN";
ESP8266WebServer server(80);
// esp-01 wiring pinout
const int LOADCELL_DOUT_PIN = 2;
const int LOADCELL_SCK_PIN = 0;
// set pulse for select channel gain
// Channel A, gain factor 128. PULSE = 1
// Channel A, gain factor 64. PULSE = 3
// Channel B, gain factor 32. PULSE = 2
const byte PULSE = 1; //channel A, 128
unsigned long rawadc, startMillis, currentMillis;
unsigned long averageraw(byte samples = 5){
unsigned long sum;
for(byte s = 0; s < samples; s++){
sum += ReadCount();
delayMicroseconds(5);
}
return sum / samples;
}
unsigned long ReadCount(void){
unsigned long Count;
unsigned char i;
digitalWrite(LOADCELL_DOUT_PIN, HIGH); // LOADCELL_DOUT_PIN set to high as output data is not ready for retrieval
digitalWrite(LOADCELL_SCK_PIN, LOW); // LOADCELL_SCK_PIN set to low as output data is not ready for retrieval
Count = 0;
while(digitalRead(LOADCELL_DOUT_PIN)); // When LOADCELL_DOUT_PIN goes to low, it indicates data is ready for retrieval.
for (i = 0; i < 24; i++){ // 24 bits are shifted out
digitalWrite(LOADCELL_SCK_PIN, HIGH);
Count = Count << 1;
digitalWrite(LOADCELL_SCK_PIN, LOW);
if(digitalRead(LOADCELL_DOUT_PIN)) Count++;
}
// set default channel A gain 128, PULSE = 1
digitalWrite(LOADCELL_SCK_PIN, HIGH);
Count = Count ^ 0x800000;
digitalWrite(LOADCELL_SCK_PIN, LOW);
//optional, 3 & 2 pulses for channel A gain 64 & channel B gain 32
if(PULSE > 1){
for(byte n = 1; n < PULSE; n++){
digitalWrite(LOADCELL_SCK_PIN, HIGH);
digitalWrite(LOADCELL_SCK_PIN, LOW);
}
}
return Count;
}
static const char HOME[] PROGMEM = R"Ganteng_Permanen(
<!DOCTYPE html>
<html>
<head>
<title>TIMBANGAN ONLEN</title>
<meta name="author" content="Ganteng Permanen">
<style>
.gauge{position:relative;background:var(--gauge-bg);border:.05em solid #222;border-radius:50%;min-width:550px;min-height:550px;font-weight:700;font-size:70px}.gauge .ticks{position:absolute;width:100%;height:100%;top:0;left:0}.gauge .ticks .min{background:#000;position:relative;left:0;top:50%;width:100%;height:1%;margin-bottom:-1%;background:linear-gradient(90deg,rgba(2,0,36,0) 0,rgba(0,0,0,0) 4%,#000 4%,#000 15%,rgba(0,0,0,0) 15%);transform:rotate(-45deg)}.gauge .ticks .mid{background:#000;position:relative;left:0;top:50%;width:100%;height:1%;margin-bottom:-1%;background:linear-gradient(90deg,rgba(2,0,36,0) 0,rgba(0,0,0,0) 4%,#000 4%,#000 15%,rgba(0,0,0,0) 15%);transform:rotate(90deg)}.gauge .ticks .max{background:#000;position:relative;left:0;top:50%;width:100%;height:1%;margin-bottom:-1%;background:linear-gradient(90deg,rgba(2,0,36,0) 0,rgba(0,0,0,0) 4%,#000 4%,#000 15%,rgba(0,0,0,0) 15%);transform:rotate(225deg)}.gauge .ticks .tithe{transform:rotate(calc(27deg * var(--gauge-tithe-tick) - 45deg));background:#000;position:relative;left:0;top:50%;width:100%;height:1%;margin-bottom:-1%;background:linear-gradient(90deg,rgba(2,0,36,0) 0,rgba(0,0,0,0) 10%,#000 10%,#000 15%,rgba(0,0,0,0) 15%)}.gauge .tick-circle{position:absolute;top:15%;left:15%;width:calc(70% - .1em);height:calc(70% - .1em);border-left:.1em solid;border-top:.1em solid;border-right:.1em solid;border-bottom:.1em solid transparent;border-radius:50%}.gauge .needle{transform:rotate(calc(270deg * calc(var(--gauge-value,0deg)/ 100) - 45deg));background:#000;position:relative;left:0;top:49%;width:100%;height:4%;margin-bottom:-4%;background:linear-gradient(90deg,rgba(2,0,36,0) 0,rgba(0,0,0,0) 24%,#000 24%,#000 30%,rgba(0,0,0,0) 50%)}.gauge .needle .needle-head{position:relative;top:15%;left:22.5%;width:2.7%;height:70%;background-color:#000;transform:rotate(-45deg)}.gauge .labels{position:absolute;width:100%;height:100%}.gauge .labels .value-label{position:relative;top:77%;text-align:center}.guide-x,.guide-y{background-color:orange;visibility:visible;position:absolute;left:50%;top:0;width:1px;height:100%}.guide-y{left:0;top:50%;width:100%;height:1px}
.gauge {
margin: auto;
}
.center {
position: absolute;
margin-left: auto;
margin-right: auto;
left: 0;
right: 0;
text-align: center;
top: 9vw;
}
span {
animation: efek .6s infinite;
font-size: 22vw;
font-weight: bold;
color: pink;
text-shadow: 2px 2px red, -2px -2px red, 0 0 18px red;
}
code {
color: pink;
opacity: 0.6;
text-shadow: 2px 2px blue;
}
@keyframes efek {
50% {text-shadow: 5px 5px 10px blue;}
}
.tab {
overflow: hidden;
margin-top: 7vw;
text-align: center;
border: 1px solid #ccc;
background-color: #f1f1f1;
}
.tab button {
background-color: inherit;
border: none;
outline: none;
cursor: pointer;
padding: 14px 16px;
transition: 0.3s;
}
.tab button:hover {
background-color: #ddd;
}
.tab button.active {
background-color: #ccc;
}
.tabcontent {
display: none;
padding: 6px 12px;
border: 1px solid #ccc;
border-top: none;
}
</style>
</head>
<body onload="init()">
<div class="center">
<span id="bobot">333</span>
<code>grams</code>
<div id="demoGauge" class="gauge" style="
--gauge-value:0;
width:200px;
height:200px;">
<div class="ticks">
<div class="tithe" style="--gauge-tithe-tick:1;"></div>
<div class="tithe" style="--gauge-tithe-tick:2;"></div>
<div class="tithe" style="--gauge-tithe-tick:3;"></div>
<div class="tithe" style="--gauge-tithe-tick:4;"></div>
<div class="tithe" style="--gauge-tithe-tick:6;"></div>
<div class="tithe" style="--gauge-tithe-tick:7;"></div>
<div class="tithe" style="--gauge-tithe-tick:8;"></div>
<div class="tithe" style="--gauge-tithe-tick:9;"></div>
<div class="min"></div>
<div class="mid"></div>
<div class="max"></div>
</div>
<div class="tick-circle"></div>
<div class="needle">
<div class="needle-head"></div>
</div>
<div class="labels">
<div class="value-label"></div>
<code id="unig" style="position:relative;top:68%;font-weight:77;font-size:14px;color:navy;">grams</code>
</div>
</div>
<div class="tab">
<button class="tablinks" onclick="setingan(event, 'tare')">Tare</button>
<button class="tablinks" onclick="setingan(event, 'calib')">Calibrate</button>
<button class="tablinks" onclick="setingan(event, 'manu')">Tuning</button>
<button class="tablinks" onclick="setingan(event, 'mode')">Mode</button>
<button class="tablinks" onclick="setingan(event, 'etc')">Bonus</button>
<button class="tablinks" onclick="setingan(event, 'noti')">Hint</button>
</div>
<div id="tare" class="tabcontent">
<label>Current Offset:</label> <b class="offsetnum"> </b>
<p>Clear the scale. Remove anything on it. Are you Ready? Hit the button now.</p>
<button type="button" id="setoffset" onclick="setup(this);">Set Offset Now!</button>
</div>
<div id="calib" class="tabcontent">
<label>Current Scale/Calibration Factor:</label> <b class="scalenum"> </b>
<p>Place a known weight on the loadcell. Next.. insert a known weight value, & hit the button.</p>
<label>Known weight <small>(grams)</small>:</label> <input type="number" id="antim" min="1" max="1000" value="50"> <button type="button" id="setscale" onclick="setup(this);">Set Scale Now!</button>
</div>
<div id="manu" class="tabcontent">
<label>Set Offset:</label> <input type="number" value="9999" class="offsetnum">
<p><label>Set Scale:</label> <input type="number" step="0.05" value="99.99" class="scalenum"></p>
<button type="button" id="setboth" onclick="setup(this);">Tweak Now!</button>
</div>
<div id="etc" class="tabcontent">
<label>Font color:</label> <input type="color" id="fontcolor" value="#ff0000" oninput="bonus(this)">
<label>Font shadow:</label> <input type="color" id="fontshadow" value="#ff0000" oninput="bonus(this)">
<p><label>Speed/Duration <small>(ms)</small>:</label> <input type="number" step="50" min="500" max="2000" value="777" id="speed" oninput="bonus(this)"></p>
<label>Delay <small>(ms)</small>:</label> <input type="number" step="100" min="500" max="3000" value="1500" id="delay" oninput="bonus(this)">
</div>
<div id="noti" class="tabcontent">
<label>Average Raw ADC Value:</label> <b id="adc"> </b>
<p>All settings are saved on browser localStorage, so.. each browser have their own saved settings.</p>
Settings will be cleared in a "private browsing" or "incognito" mode.
</div>
<div id="mode" class="tabcontent">
<input type="radio" name="modecek" id="normal" onclick="setmode(this)"> <label for="normal">Normal</label>
<input type="radio" name="modecek" id="filler" onclick="setmode(this)"> <label for="filler">Filler</label><br><br>
<div id="normalval" style="display:block;"><input type="checkbox" id="sgg" onchange="setmode(this)"> <label for="sgg">Show Gauge</label></div>
<div id="thresholdval" style="display:none;"><i id="curthres"></i><br><br><label>Threshold <small>(Max 1000 grams)</small>:</label>
<input type="number" value="500" min="1" max="1000" id="maxthreshold"> <button type="button" id="setthreshold" onclick="setup(this);">Set Threshold Now!</button></div>
</div>
</div>
<small>Maks beban = 1kg</small>
<script>
const Storageprefix = "test1-";
const scaleStorageitemname = Storageprefix + "scale";
const offsetStorageitemname = Storageprefix + "offset";
const durasiStorageitemname = Storageprefix + "durasi";
const rehatStorageitemname = Storageprefix + "rehat";
const colorStorageitemname = Storageprefix + "color";
const shadowStorageitemname = Storageprefix + "shadow";
const modeStorageitemname = Storageprefix + "mode";
const thresholdStorageitemname = Storageprefix + "threshold";
const showgaugeStorageitemname = Storageprefix + "showgauge";
var durasi = 777; // animation counting duration
var rehat = 1500; // delay for next animation counter
var x = 33; // total steps
var i = 0; // step
var newres = 0; // new value
var oldres = 0; // old value
var margin = 0; // absolute value
var step = 0; // step value
var rawvalue; //raw reading
var offset = "0"; //
var rawunit; // rawvalue - offset
var unitval; // rawunit / scale
var scale = "1"; // rawunit / bobot
var fontkolor = "#FF1493", fontsado = "#FF0000";
var mode = "normal"; //
var threshold = 500; //
var showgauge = false;
//get saved settings on localStorage
function init() {
(localStorage.getItem(offsetStorageitemname) == null) ? localStorage.setItem(offsetStorageitemname, offset) : offset = localStorage.getItem(offsetStorageitemname);
document.getElementsByClassName("offsetnum")[0].innerHTML = offset;
document.getElementsByClassName("offsetnum")[1].value = offset;
(localStorage.getItem(scaleStorageitemname) == null) ? localStorage.setItem(scaleStorageitemname, scale) : scale = localStorage.getItem(scaleStorageitemname);
document.getElementsByClassName("scalenum")[0].innerHTML = scale;
document.getElementsByClassName("scalenum")[1].value = scale;
(localStorage.getItem(colorStorageitemname) == null) ? localStorage.setItem(colorStorageitemname, fontkolor) : fontkolor = localStorage.getItem(colorStorageitemname);
document.getElementsByTagName("span")[0].style.color = fontkolor;
document.getElementById("fontcolor").value = fontkolor;
(localStorage.getItem(shadowStorageitemname) == null) ? localStorage.setItem(shadowStorageitemname, fontsado) : fontsado = localStorage.getItem(shadowStorageitemname);
document.getElementsByTagName("span")[0].style.textShadow = "2px 2px " +fontsado+ ", -2px -2px " +fontsado+ ", 0 0 18px " + fontsado;
document.getElementById("fontshadow").value = fontsado;
(localStorage.getItem(durasiStorageitemname) == null) ? localStorage.setItem(durasiStorageitemname, durasi) : durasi = localStorage.getItem(durasiStorageitemname);
document.getElementById("speed").value = durasi;
(localStorage.getItem(rehatStorageitemname) == null) ? localStorage.setItem(rehatStorageitemname, rehat) : rehat = localStorage.getItem(rehatStorageitemname);
document.getElementById("delay").value = rehat;
(localStorage.getItem(modeStorageitemname) == null) ? localStorage.setItem(modeStorageitemname, mode) : mode = localStorage.getItem(modeStorageitemname);
document.getElementById(mode).checked = true;
displaythreshold(mode);
(localStorage.getItem(thresholdStorageitemname) == null) ? localStorage.setItem(thresholdStorageitemname, threshold) : threshold = localStorage.getItem(thresholdStorageitemname);
document.getElementById("maxthreshold").value = threshold;
document.getElementById("curthres").innerHTML = "Current Max. Threshold:<mark> " + threshold + " </mark>grams";
(localStorage.getItem(showgaugeStorageitemname) == null) ? localStorage.setItem(showgaugeStorageitemname, showgauge) : showgauge = localStorage.getItem(showgaugeStorageitemname);
document.getElementById("sgg").checked = (showgauge === "true") ? true : false;
gaugeon(document.getElementById("sgg").checked);
document.getElementsByClassName("tab")[0].style.marginTop = (showgauge === "true") ? "1vw" : "7vw";
}
var intervalKonter = setInterval(Konter, durasi / x);
function Konter() {
i += 1;
if(i == 1) {
margin = Math.abs(newres.toFixed(2) - oldres);
x = margin < 3 ? 12 : 33;
step = margin / x;
document.getElementsByTagName("span")[0].style.animationPlayState = "paused";
}
newres > oldres ? oldres += step : oldres -= step;
if(oldres < 0) oldres = 0;
//document.getElementById("bobot").innerHTML = oldres.toFixed(2);
document.getElementById("bobot").innerHTML = (mode == "filler") ? (oldres/(threshold/100)).toFixed(1) : oldres.toFixed(2) ;
document.getElementsByClassName("value-label")[0].innerHTML = oldres.toFixed(2);
var jarum = Math.floor((oldres / 1000) * 100);
document.getElementById("demoGauge").style.setProperty('--gauge-value', jarum);
if(i == x) {
oldres = newres;
document.getElementsByTagName("span")[0].style.animationPlayState = "running";
//rawvalue = 1000 * Math.random(); // simulate sensor weight value, replace w/ xhr
//rawvalue = Math.floor(Math.random() * 999999); // simulate sensor raw adc value
fetch("hx").then(hasil => hasil.text()).then(bait => rawvalue = parseInt(bait));
rawunit = rawvalue - offset;
if(rawunit < 0) rawunit = 0;
newres = rawunit / scale;
document.getElementById("adc").innerHTML = rawvalue;
clearInterval(intervalKonter);
i = 0;
setTimeout (function() {intervalKonter = setInterval(Konter, durasi / x);}, rehat);
}
}
function gaugeon(ok) {
if(ok === true){
document.getElementById("bobot").style.display = "none";
document.getElementsByTagName("code")[0].style.display = "none";
document.getElementById("demoGauge").style.display = "block";
document.getElementsByClassName("center")[0].style.top = "5px";
}else{
document.getElementById("bobot").style.display = "initial";
document.getElementsByTagName("code")[0].style.display = "initial";
document.getElementById("demoGauge").style.display = "none";
document.getElementsByClassName("center")[0].style.top = "7vw";
}
localStorage.setItem(showgaugeStorageitemname, ok);
}
function displaythreshold(id) {
if(id == "filler"){
document.getElementById("normalval").style.display = "none";
document.getElementById("thresholdval").style.display = "block";
document.getElementsByTagName("code")[0].innerHTML = "%";
document.getElementsByTagName("code")[0].style.fontSize = "5vw";
gaugeon(false);
document.getElementById("sgg").checked = false;
}else{
document.getElementById("normalval").style.display = "block";
document.getElementById("thresholdval").style.display = "none";
document.getElementsByTagName("code")[0].innerHTML = "grams";
document.getElementsByTagName("code")[0].style.fontSize = "initial";
}
}
function setmode(radio) {
mode = radio.id;
if(mode === "sgg"){
gaugeon(radio.checked);
}else{
displaythreshold(mode);
localStorage.setItem(modeStorageitemname, mode);
}
}
function setup(tombol) {
if(tombol.id == "setthreshold"){
threshold = document.getElementById("maxthreshold").value;
localStorage.setItem(thresholdStorageitemname, threshold);
document.getElementById("curthres").innerHTML = "Current Max. Threshold:<mark> " + threshold + " </mark>grams";
}else{
offset = document.getElementsByClassName("offsetnum")[1].value;
scale = document.getElementsByClassName("scalenum")[1].value;
if(tombol.id === "setoffset" || tombol.id === "setboth"){
if(tombol.id === "setoffset") offset = rawvalue;
localStorage.setItem(offsetStorageitemname, offset);
document.getElementsByClassName("offsetnum")[0].innerHTML = offset;
document.getElementsByClassName("offsetnum")[1].value = offset;
}
if(tombol.id === "setscale" || tombol.id === "setboth"){
if(tombol.id === "setscale") scale = rawunit / document.getElementById("antim").value;
localStorage.setItem(scaleStorageitemname, scale);
document.getElementsByClassName("scalenum")[0].innerHTML = scale;
document.getElementsByClassName("scalenum")[1].value = scale;
}
}
}
function setingan(evt, opsi) {
var u, tabcontent, tablinks, bol;
if(document.getElementById(opsi).style.display == "block") {
bol = 1;
if(document.getElementById("bobot").style.display !== "none"){
document.getElementsByClassName("tab")[0].style.marginTop = "7vw";
document.getElementsByClassName("center")[0].style.top = "9vw";
}else{
document.getElementsByClassName("gauge")[0].style.minWidth = "550px";
document.getElementsByClassName("gauge")[0].style.minHeight = "550px";
document.getElementsByClassName("gauge")[0].style.fontSize = "70px";
document.getElementById("unig").style.fontSize = "14px";
document.getElementsByClassName("tab")[0].style.marginTop = "1vw";
}
}
tabcontent = document.getElementsByClassName("tabcontent");
for (u = 0; u < tabcontent.length; u++) {
tabcontent[u].style.display = "none";
}
tablinks = document.getElementsByClassName("tablinks");
for (u = 0; u < tablinks.length; u++) {
tablinks[u].className = tablinks[u].className.replace(" active", "");
}
if(bol !== 1){
if(document.getElementById("bobot").style.display !== "none"){
document.getElementsByClassName("center")[0].style.top = "7vw";
document.getElementsByClassName("tab")[0].style.marginTop = "1vw";
}else{
document.getElementsByClassName("gauge")[0].style.minWidth = "463px";
document.getElementsByClassName("gauge")[0].style.minHeight = "463px";
document.getElementsByClassName("gauge")[0].style.fontSize = "45px";
document.getElementById("unig").style.fontSize = "10px";
document.getElementsByClassName("tab")[0].style.marginTop = "1vw";
}
document.getElementById(opsi).style.display = "block";
evt.currentTarget.className += " active";
}
}
function bonus(selek) {
switch(selek.id) {
case "speed":
durasi = selek.value;
if(selek.value < 500) durasi = 500;
if(selek.value > 2000) durasi = 2000;
localStorage.setItem(durasiStorageitemname, durasi)
break;
case "delay":
rehat = selek.value;
if(selek.value < 500) rehat = 500;
if(selek.value > 3000) rehat = 3000;
localStorage.setItem(rehatStorageitemname, rehat)
break;
case "fontcolor":
document.getElementsByTagName("span")[0].style.color = selek.value;
localStorage.setItem(colorStorageitemname, selek.value);
break;
case "fontshadow":
document.getElementsByTagName("span")[0].style.textShadow = "2px 2px " +selek.value+ ", -2px -2px " +selek.value+ ", 0 0 18px " + selek.value;
localStorage.setItem(shadowStorageitemname, selek.value);
break;
}
}
</script>
</body>
</html>
)Ganteng_Permanen";
/* go to http://192.168.4.1 via web browser */
void handleRoot() {
server.send_P(200, "text/html", HOME);
}
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
WiFi.softAP(ssid);
pinMode(LOADCELL_DOUT_PIN, INPUT);
pinMode(LOADCELL_SCK_PIN, OUTPUT);
while(digitalRead(LOADCELL_DOUT_PIN)); // wait tobe ready
//initial reading
rawadc = ReadCount();
server.on("/", handleRoot);
server.on("/hx", []() {
//300ms & up for next sensor reading
currentMillis = millis();
if (currentMillis - startMillis >= 300) {
rawadc = averageraw(4);
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
startMillis = currentMillis;
}
delay(2);
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "-1");
server.send(200, "text/plain", String(rawadc)); // let the browser to calculate & measuring the weight
});
server.begin();
}
void loop() {
server.handleClient();
}
///////////////////////////////////////////////
// ©2022 //
// https://www.youtube.com/c/GantengPermanen //
// USE @ YOUR OWN RISK //
///////////////////////////////////////////////