Wireless hydrometer with an ESP32 powered by MicroPython
A digital wireless hydrometer inspired by Tilt & iSpindel.
Powered by MicroPython ESP32.
** Just for reference as this is no hardware building guide.
uftpd.py
to ESP32, pls see the tutorial
> import network
> sta = network.WLAN(network.STA_IF)
> sta.active(True)
> sta.connect('WIFI_SSID', 'WIFI_PASSWORD') # replace the strings with your own wifi ssid and password
> sta.isconnected() # check if the wifi connection is established
> import uftpd # if wifi is connected, import uftpd to start the FTP service, you will see the ip and port of the ftp server
torpedo
folder to the ESP32.Hydrometer
, and visit 192.168.4.1
with your browser.The purpose of the calibration is to find out the relationship between the tilt angle of the tube floating in the
wort and the specific gravity (or Plato), which is polynomial, e.g. gravity = a * tilt * tilt + b * tilt + c
.
By measuring tilt angles of the tube in sugar solution with different concentration, the parameters a, b, c can be
solved via regression. The parameters will be affected by the mass and the weight distribution of the hydrometer,
so once you have changed the weight of the hydrometer or the shape/size of the tube, you will need to
redo the calibration.
hardware_config.json
.HTTP API
: if you wish to use the hydrometer with CraftBeerPi3,
this is how you will send your hydrometer data. Alternatively, if you write your own fermentation controller and
wish to receive the hydrometer data via HTTP protocol, this is the choice.MQTT
: if you wish to publish the data to an IoT platform (e.g. Ubidots) to visualize your hydrometer data, this
is your choice.Below is an example of the JSON content to be sent via HTTP POST
method to the API every time it wakes up.
{
"name": "Hydrometer",
"ID": 748215638,
"temperature": 22.3,
"angle": 67.5,
"battery": 3.76,
"fahrenheit": 72.1,
"currentGravity": 1.043,
"currentPlato": 10.7,
"batteryLevel": 90,
"updateIntervalMs": 1200000
}
name
: string; The name of the hydrometer, it's also the SSID of the WiFi AP signal of the hydrometer.ID
: integer; The unique machine code of the ESP32.temperature
: number; The temperature measured by the DS18B20, unit: Celsius.angle
: number; The tilt angle of the long axis of the tube, unit degree.battery
: number; The voltage of the battery, unit: V.fahrenheit
: number; The temperature measured by the DS18B20, unit: Fahrenheit.currentGravity
: number; The calculated specific gravity of the wort, unit: SG.currentPlato
: number; The calculated Plato of the wort, unit: °P.batteryLevel
: number; The percentage of the battery level, unit: %.updateIntervalMs
: integer; The wake up interval, unit: ms (milli-second).If you set the hydrometer to send the data via MQTT, below is an example of the data which will be sent.
{
"temperature": 22.3,
"sg": 1.043,
"plato": 10.7,
"battery": 3.76
}
temperature
: number; The temperature measured by the DS18B20, unit: Celsius.sg
: number; The calculated specific gravity of the wort, unit: SG.plato
: number; The calculated Plato of the wort, unit: °P.battery
: number; The voltage of the battery, unit: V.In case the WiFi sigal is too weak to penetrate your fermenter or fridge, you need an external antenna to relay the signal, which is extremely easy to build. All you need is an insulated single-core electric wire - strip 31mm (1/4 Lambda) of the insulated layers at both ends. Make sure one end of the wire is placed close enough to the hydrometer, and the other end close to the fermenter controller or the WiFi router, so it acts as 'a bridge'.
{
"unit": "p",
"a": 0.016695786886914407,
"b": -1.7010962658032376,
"c": 0.016695786886914407
}
{
"currentGravity": 1.057,
"batteryLevel": 60,
"updateIntervalSec": 1200
}
// *注意:发酵箱显示的比重默认单位为sg,读取比重计数据时要先根据情况转换单位。
{
"sg": 1.057,
"battery": 60
}
chartDataSeries: {
setTempDataList: [],
chamberTempDataList: [],
wortTempDataList: [],
gravityDataList: [],
}
cg = corrected specific gravity
mg = measured specific gravity
tr = temperature at time of reading (F)
tc = calibration temperature of hydrometer (F)
cg = mg * ((1.00130346 - 0.000134722124 * tr + 0.00000204052596 * tr^2 - 0.00000000232820948 * tr^3) / (1.00130346 - 0.000134722124 * tc + 0.00000204052596 * tc^2 - 0.00000000232820948 * tc^3))
//ecStat 是 ECharts 的统计扩展,需要额外添加扩展脚本,参加上方“脚本”
// 详情请移步 https://github.com/ecomfe/echarts-stat
// 图中的曲线是通过多项式回归拟合出的
var data = [
[27.4, 1.0],
[61.25, 1.074],
[62.63, 1.077],
[60.6, 1.071],
[58.85, 1.067],
[52, 1.048],
[48.4, 1.039],
[42.4, 1.027],
[39, 1.020],
[32, 1.008]
];
var myRegression = ecStat.regression('polynomial', data, 2);
console.log(myRegression.expression)
console.log(myRegression.parameter)
myRegression.points.sort(function(a, b) {
return a[0] - b[0];
});
myChart.setOption({
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
}
},
title: {
text: 'Tilt-Gravity Regression',
left: 'center',
top: 16
},
xAxis: {
type: 'value',
name: 'Tilt Angle',
nameLocation: 'middle',
nameGap: 30,
min: 20,
splitLine: {
lineStyle: {
type: 'dashed'
}
},
splitNumber: 20
},
yAxis: {
type: 'value',
name: 'Gravity',
nameLocation: 'middle',
nameGap: 40,
min: 0.99,
splitLine: {
lineStyle: {
type: 'dashed'
}
},
splitNumber: 10
},
series: [{
name: 'scatter',
type: 'scatter',
label: {
emphasis: {
show: true
}
},
data: data
}, {
name: 'line',
type: 'line',
smooth: true,
showSymbol: false,
data: myRegression.points,
markPoint: {
itemStyle: {
normal: {
color: 'transparent'
}
},
label: {
normal: {
show: true,
position: 'left',
formatter: myRegression.expression,
textStyle: {
color: '#333',
fontSize: 14
}
}
},
data: [{
coord: myRegression.points[myRegression.points.length - 1]
}]
}
}]
});
{
"tilt": 76.3
}
{
"unit": "p",
"a": 0.016695786886914407,
"b": -1.7010962658032376,
"c": 0.016695786886914407
}
{
"deepSleepIntervalMs": 10000,
"apSsid": "Hydrometer",
"wifi": {
"ssid": "",
"pass": ""
},
"fermenterAp": {
"enabled": true,
"ssid": "Fermenter",
"pass": ""
},
"mqtt": {
"enabled": false,
"brokerAddr": "things.ubidots.com",
"brokerPort": 1883,
"username": "BBA-DASKLFJELFEL5646566WW",
"password": "",
"topic": "/v1.6/devices/hydrometer"
},
"wifiList": [
"28#301",
"28#301_ASUS",
"Fermenter",
"ChinaNet1234",
"ChinaNet4542",
"UnionCom8876"
]
}
{
"deepSleepIntervalMs": 10000,
"apSsid": "Hydrometer",
"wifi": {
"ssid": "",
"pass": ""
},
"fermenterAp": {
"enabled": true,
"ssid": "Fermenter",
"pass": ""
},
"mqtt": {
"enabled": false,
"brokerAddr": "things.ubidots.com",
"brokerPort": 1883,
"username": "BBA-DASKLFJELFEL5646566WW",
"password": "",
"topic": "/v1.6/devices/hydrometer"
}
}
{
"wifiList": [
"28#301",
"28#301_ASUS",
"Fermenter",
"ChinaNet1234",
"ChinaNet4542",
"UnionCom8876"
]
}
{
"ssid": "ChinaNet4542",
"pass": "12345678"
}
向MQTT服务器发送测试信息
{
"mqtt": {
"enabled": true,
"brokerAddr": "things.ubidots.com",
"brokerPort": 1883,
"username": "BBA-DASKLFJELFEL5646566WW",
"password": "",
"topic": "/v1.6/devices/hydrometer"
}
}