Skip to content

Instantly share code, notes, and snippets.

@kenwebb
Last active May 6, 2023 13:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kenwebb/ea03a54c5c871ecd1563574f8b718b6e to your computer and use it in GitHub Desktop.
Save kenwebb/ea03a54c5c871ecd1563574f8b718b6e to your computer and use it in GitHub Desktop.
Pumped Hydro
<?xml version="1.0" encoding="UTF-8"?>
<!--Xholon Workbook http://www.primordion.com/Xholon/gwt/ MIT License, Copyright (C) Ken Webb, Sat May 06 2023 09:03:54 GMT-0400 (Eastern Daylight Saving Time)-->
<XholonWorkbook>
<Notes><![CDATA[
Xholon
------
Title: Pumped Hydro
Description:
Url: http://www.primordion.com/Xholon/gwt/
InternalName: ea03a54c5c871ecd1563574f8b718b6e
Keywords:
My Notes
--------
4 May 2023
In this workbook, I want to:
- build a simple Xholon model based on the ideas and ojects in the NYTimes article
- use hardware sensors to measure real light levels in the house or outside
- use the generated data to decide what to do in the model
- use simulated solar energy when light is available
- use simulated pumped hydro when light levels are low or non-existent
I will use mqtt, node-red, grove sensors, picow/arduino/pizero
- see my folders from late 2022 and early 2023, and my mqtt01 gist (14 Jan 2023)
Write down all the steps that I used at that time.
I was able to get the sensor data all the way to a running Xholon app.
Steps
=====
node-red (not required)
--------
cd
node-red
4 May 08:51:30 - [info]
Welcome to Node-RED
....
Server now running at http://127.0.0.1:1880/
....
opened /tmp/sqlite ok
Connected to broker: mqtt://localhost:1883
- Flow 5 is active
- it is connected to mosquitto broker and sqlite which are both automatically available
Grove Beginner Kit
------------------
Arduino code:
~/Arduino/seeed/sketch_jan18d_KenzEnviro03/sketch_jan18d_KenzEnviro03.ino
- with Arduino on Grove kit plugged into my HP ZBook USB port
minicom -b 115200 -o -D /dev/ttyUSB0
- this works
OR (this works):
stty -F /dev/ttyUSB0 115200 raw -clocal -echo
mosquitto_pub -h 192.168.1.15 -t KenzEnviro/thl -l < /dev/ttyUSB0
Xholon workook mqtt01
---------------------
http://127.0.0.1:8081/Xholon.html?app=mqtt01&src=lstr&gui=clsc&jslib=mqttws31
http://127.0.0.1:8081/war/Xholon.html?app=mqtt01&src=lstr&gui=clsc&jslib=mqttws31.min
- this sort-of works (but with an error that causes no data acquired)
- ut see 5 May below :)
5 May 2023 - this works
-----------------------
- I haven't changed any code, but the following sequence of events now works
(1) plug the Grove Beginner Kit into the HP ZBook USB
(2) in terminal A (base) ken@ken-HP-ZBook-15-G2:~$
stty -F /dev/ttyUSB0 115200 raw -clocal -echo
mosquitto_pub -h 192.168.1.15 -t KenzEnviro/thl -l < /dev/ttyUSB0
(3) in terminal B (base) ken@ken-HP-ZBook-15-G2:~$
mosquitto_sub -d -t KenzEnviro/thl
- this visually confirms that I can get correct data
Client null sending CONNECT
Client null received CONNACK (0)
Client null sending SUBSCRIBE (Mid: 1, Topic: KenzEnviro/thl, QoS: 0, Options: 0x00)
Client null received SUBACK
Subscribed (mid: 1): 0
Client null received PUBLISH (d0, q0, r0, m0, 'KenzEnviro/thl', ... (89 bytes))
{"t":23.4,"h":31.5,"l":10,"s":109,"m":0,"w":"OfK","p":"aunogr","r":[2023,5,5,5,9,56,54]}
Client null sending PINGREQ
Client null received PINGRESP
Client null received PUBLISH (d0, q0, r0, m0, 'KenzEnviro/thl', ... (88 bytes))
{"t":23.4,"h":31.5,"l":9,"s":121,"m":0,"w":"OfK","p":"aunogr","r":[2023,5,5,5,9,57,54]}
(4) http://127.0.0.1:8081/Xholon.html?app=mqtt01&src=lstr&gui=clsc&jslib=mqttws31
Connecting to: 192.168.1.15 on port: 8080
Subscribing to topic: KenzEnviro/thl
Temperature,Humidity,Light,Sound,Motion,Where,Datetime
23.4,31.4,8,70,0,OfK,05/05/2023T09:58:54
23.4,31.4,7,92,0,OfK,05/05/2023T09:59:54
23.4,31.5,7,51,0,OfK,05/05/2023T10:00:54
23.4,31.6,7,69,0,OfK,05/05/2023T10:01:54
23.5,31.6,6,64,0,OfK,05/05/2023T10:02:54
23.5,31.5,7,91,0,OfK,05/05/2023T10:03:54
23.5,31.5,7,123,0,OfK,05/05/2023T10:04:54
23.5,31.6,9,30,0,OfK,05/05/2023T10:05:54
23.5,31.6,7,71,0,OfK,05/05/2023T10:06:54
23.5,31.6,7,128,0,OfK,05/05/2023T10:07:54
23.5,31.6,7,108,0,OfK,05/05/2023T10:08:54
23.5,31.5,9,113,0,OfK,05/05/2023T10:09:54
How to run the Pumped Hydro workbook app
----------------------------------------
http://127.0.0.1:8081/Xholon.html?app=Pumped+Hydro&src=lstr&gui=clsc&jslib=mqttws31
Connecting to: 192.168.1.15 on port: 8080
water_50 0
water_54 1000000
Subscribing to topic: KenzEnviro/thl
Temperature,Humidity,Light,Sound,Motion,Where,Datetime
24.6,32.3,27,127,0,OfK,05/05/2023T13:38:55
27 999973 27
24.5,32.1,26,156,0,OfK,05/05/2023T13:39:55
26 999947 53
24.5,32,25,165,0,OfK,05/05/2023T13:40:55
25 999922 78
24.5,31.8,26,150,0,OfK,05/05/2023T13:41:55
26 999896 104
24.4,31.8,24,64,0,OfK,05/05/2023T13:42:55
24 999872 128
SVG visualization
-----------------
- see Coin Flipping Example http://127.0.0.1:8081/wb/editwb.html?app=2159078&src=gist
References
----------
() https://www.nytimes.com/interactive/2023/05/02/climate/hydroelectric-power-energy.html
NYTimes article, 2 May
Is It a Lake, or a Battery? A New Kind of Hydropower Is Spreading Fast.
By Mira Rojanasakul and Max BearakMay 2, 2023
These systems involve two reservoirs: one on top of a hill and another at the bottom.
When electricity generated from nearby power plants exceeds demand,
it’s used to pump water uphill, essentially filling the upper reservoir as a battery.
Later, when electricity demand spikes,
water is released to the lower reservoir through a turbine, generating power.
() my notebook 2 May 2023
]]></Notes>
<_-.XholonClass>
<PhysicalSystem/>
<ThlClient/>
<Reservoir/>
<Hill/>
<TopOfHill/>
<BottomOfHill/>
<Turbine/>
<Water/>
<ElectricitySource>
<SolarPanel/>
<WindTurbine/>
<FossilFuelPlant/>
</ElectricitySource>
</_-.XholonClass>
<xholonClassDetails>
</xholonClassDetails>
<PhysicalSystem>
<ThlClient host="192.168.1.15" port="8080" clientidPrefix="clientID-" topic="KenzEnviro/thl" solarPanel=""/>
<Hill>
<TopOfHill>
<Reservoir roleName="top">
<Water count="0"/>
</Reservoir>
</TopOfHill>
<Turbine/> <!-- converts falling water into electricity -->
<BottomOfHill>
<Reservoir roleName="bottom">
<Water count="5000"/>
</Reservoir>
</BottomOfHill>
</Hill>
<SolarPanel/> <!-- converts light into electricity, and pumps water uphill -->
</PhysicalSystem>
<ThlClientbehavior implName="org.primordion.xholon.base.Behavior_gwtjs"><![CDATA[
var me, beh = {
postConfigure: function() {
me = this.cnode.parent();
$wnd.xh.param("TimeStepInterval", 500);
this.startConnect();
},
startConnect: function() {
me.clientID = me.clientidPrefix + parseInt(Math.random() * 100);
me.println("Connecting to: " + me.host + " on port: " + me.port);
me.client = new $wnd.Paho.MQTT.Client(me.host, Number(me.port), me.clientID);
me.client.onConnectionLost = this.onConnectionLost;
me.client.onMessageArrived = this.onMessageArrived;
me.client.connect({
onSuccess: this.onConnect,
//userName:"USERNAME",
//password:"PASSWORD"
});
},
onConnect: function() {
me.println("Subscribing to topic: " + me.topic);
me.client.subscribe(me.topic);
me.println("Temperature,Humidity,Light,Sound,Motion,Where,Datetime");
},
onConnectionLost: function(responseObject) {
me.println("Connection Lost");
if (responseObject.errorCode !== 0) {
me.println("onConnectionLost: " + responseObject.errorMessage);
}
},
onMessageArrived: function(message) {
//me.println("Topic: " + message.destinationName + " " + message.payloadString);
beh.processPayload(message.payloadString);
},
startDisconnect: function() {
me.client.disconnect();
me.println("Disconnected");
},
processPayload: function(json) {
// json is a JSON Oject ex: {"t":24.2,"h":33.0,"l":1248} {"t":24.3,"h":37.0,"l":40265,"m":0,"r":[2023, 1, 9, 1, 11, 49, 3]}
var jsobj = JSON.parse(json);
var dt = jsobj.r.map(ele => ele < 10 ? "0" + ele : "" + ele);
me.println(`${jsobj.t},${jsobj.h},${jsobj.l},${jsobj.s},${jsobj.m},${jsobj.w},${dt[1]}/${dt[2]}/${dt[0]}T${dt[4]}:${dt[5]}:${dt[6]}`);
me.solarPanel.msg(101, jsobj.l.toString());
}
}
//# sourceURL=ThlClientbehavior.js
]]></ThlClientbehavior>
<SolarPanelbehavior implName="org.primordion.xholon.base.Behavior_gwtjs"><![CDATA[
var me, top, bottom, rectSVG, beh = {
postConfigure: function() {
me = this.cnode.parent();
const lightSource = me.parent().first(); // ThlClient
lightSource.solarPanel = this.cnode;
top = me.parent().first().next().first().first().first();
bottom = me.parent().first().next().first().next().next().first().first();
top.count = Number(top.count);
bottom.count = Number(bottom.count);
me.println(top.name() + " " + top.count);
me.println(bottom.name() + " " + bottom.count);
},
processReceivedMessage: function(msg) {
var amount = Number(msg.data);
// move msg.data amount of water from bottom to top reservoir
bottom.count -= amount;
top.count += amount;
me.println("" + amount + " " + bottom.count + " " + top.count);
// SVG
if (!rectSVG) {
var svg = $doc.getElementById("xhsvg").firstElementChild.firstElementChild;
rectSVG = svg.getElementById("PhysicalSystem/Hill/TopOfHill");
}
rectSVG.setAttributeNS(null, 'height', top.count * 0.1);
$wnd.console.log('height', top.count, top.count * 0.1, rectSVG);
}
}
//# sourceURL=SolarPanelbehavior.js
]]></SolarPanelbehavior>
<SvgClient><Attribute_String roleName="svgUri"><![CDATA[data:image/svg+xml,
<svg width="100" height="500" xmlns="http://www.w3.org/2000/svg">
<g>
<title>PhysicalSystem</title>
<rect id="PhysicalSystem/Hill/BottomOfHill" fill="#1E90FF" height="500" width="50" x="25" y="0"/>
<rect id="PhysicalSystem/Hill/TopOfHill" fill="#00BFFF" height="1" width="50" x="25" y="0"/>
</g>
</svg>
]]></Attribute_String><Attribute_String roleName="setup">${MODELNAME_DEFAULT},${SVGURI_DEFAULT}</Attribute_String></SvgClient>
</XholonWorkbook>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment