Skip to content

Commit b2ca125

Browse files
cluckjscottleibrand
authored andcommitted
Bugfixes, Radiofruit bonnet support (#26)
* Add relevant OR part as well The relevant "OR echo '{"batteryVoltage":3340,"battery":99}'" is still missing. So the script still crashes NightScout in its current state. Now added that line and the explaining line of comment as well. Tested for several days on Pi & RadioFruit Bonnet combo with two different NS instances. * Support for Adafruit Radiofruit RFM69HCW Bonnet * Update getvoltage.sh * Unified Explorer HAT and Radiofruit configs, blank status screen script option (effectively puts the screen to sleep but leaves menus working) * Add carbs required to warnings line * Update README.md New status screen option. * Text-only radiofruit status screen * Undefined check Crash prevention... * Oops * Capitalization * Temp basal mismatch loop failure message. * Low pump battery warning. * Update dependencies to latest. * Typo
1 parent 30fac3d commit b2ca125

File tree

11 files changed

+294
-37
lines changed

11 files changed

+294
-37
lines changed

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
# openaps-menu
22

3-
This is the repository holding the menu-based software code, which you may choose to add to an Explorer HAT or other screen-based rig in order to visualize and enter information into a Pi-based #OpenAPS rig.
3+
This is the repository holding the menu-based software code, which you may choose to add to an Explorer HAT or other screen-based rig (e.g., Adafruit Radiofruit Bonnet) in order to visualize and enter information into a Pi-based #OpenAPS rig.
44

55
See [here](https://github.com/EnhancedRadioDevices/Explorer-HAT) for more details on the Explorer HAT hardware.
66

77
You can set your preferred auto-updating status screen using the following setting in your `~/myopenaps/preferences.json`:
88

99
`"status_screen": "bigbgstatus"` will display the big BG status screen (no graph).
1010

11-
`"status_screen": "off"` will turn the auto-updating screen off.
11+
`"status_screen": "off"` will not auto-update the status screen.
1212

13+
`"status_screen": "blank"` will wipe the screen during the auto-update, but will wake up when you press a button to let you access menu options.
1314

1415
By default, the auto-updating status script will invert the display about 50% of the time, to prevent burn-in on the OLED screen. You can turn this off with the following setting in your `~/myopenaps/preferences.json`:
1516

File renamed without changes.

config/buttons-radiofruit.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"gpios": {
3+
"buttonUp": 5,
4+
"buttonDown": 6
5+
},
6+
"options": {
7+
"socketPath": "/var/run/pi-buttons.sock",
8+
"reconnectTimeout": 3000
9+
}
10+
}

config/display.json

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
{
2-
"title": " ",
3-
"height": 64,
4-
"displayLines": 8
2+
"radiofruit": {
3+
"title": " ",
4+
"height": 32,
5+
"displayLines": 4
6+
},
7+
"explorerHat": {
8+
"title": " ",
9+
"height": 64,
10+
"displayLines": 8
11+
}
512
}

index.js

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,41 @@ var i2cBus = i2c.openSync(1);
1616

1717
var openapsDir = "/root/myopenaps"; //if you're using a nonstandard OpenAPS directory, set that here. NOT RECOMMENDED.
1818

19-
// setup the display
20-
var displayConfig = require('./config/display.json');
19+
try {
20+
var preferences = JSON.parse(fs.readFileSync(openapsDir+"/preferences.json"));
21+
} catch (e) {
22+
console.error("Could not load preferences.json", e);
23+
}
24+
25+
// setup the display, depending on its size (Eadiofruit is 128x32 and Explorer HAT is 128x64)
26+
if (preferences.hardwaretype && preferences.hardwaretype == "radiofruit") {
27+
var displayConfig = require('./config/display.json').radiofruit;
28+
} else {
29+
var displayConfig = require('./config/display.json').explorerHat;
30+
}
31+
2132
displayConfig.i2cBus = i2cBus;
2233

2334
try {
2435
var display = require('./lib/display/ssd1306')(displayConfig);
25-
displayImage('./static/unicorn.png'); //display logo
36+
if (preferences.hardwaretype && preferences.hardwaretype == "radiofruit") {
37+
displayImage('./static/unicorn_128x32.png');
38+
} else {
39+
displayImage('./static/unicorn_128x64.png');
40+
}
2641
} catch (e) {
2742
console.warn("Could not setup display:", e);
2843
}
2944

45+
function displayImage(pathToImage) {
46+
pngparse.parseFile(pathToImage, function(err, image) {
47+
if(err)
48+
throw err
49+
display.clear();
50+
display.oled.drawBitmap(image.data);
51+
});
52+
}
53+
3054
// setup battery voltage monitor
3155
var voltageConfig = require('./config/voltage.json')
3256
voltageConfig.i2cBus = i2cBus
@@ -47,37 +71,34 @@ socketServer
4771
})
4872
.on('displaystatus', function () {
4973
if (display) {
50-
var preferences;
51-
fs.readFile(openapsDir+'/preferences.json', function (err, data) {
52-
if (err) throw err;
53-
preferences = JSON.parse(data);
5474
if (preferences.status_screen && preferences.status_screen == "bigbgstatus") {
5575
bigBGStatus(display, openapsDir);
5676
} else if (preferences.status_screen && preferences.status_screen == "off") {
5777
//don't auto-update the screen if it's turned off
78+
} else if (preferences.status_screen && preferences.status_screen == "blank") {
79+
display.clear(true);
80+
} else if (preferences.hardwaretype && preferences.hardwaretype == "radiofruit") {
81+
radiofruitStatus(display, openapsDir); //radiofruit text status script
5882
} else {
5983
graphStatus(display, openapsDir); //default to graph status
6084
}
61-
});
6285
}
6386
})
6487

65-
function displayImage(pathToImage) {
66-
pngparse.parseFile(pathToImage, function(err, image) {
67-
if(err)
68-
throw err
69-
display.clear();
70-
display.oled.drawBitmap(image.data);
71-
});
72-
}
73-
7488
// load up graphical status scripts
7589
const graphStatus = require('./scripts/status.js');
7690
const bigBGStatus = require('./scripts/big_bg_status.js');
91+
const radiofruitStatus = require('./scripts/status-radiofruit.js');
7792
// if you want to add your own status display script, it will be easiest to replace one of the above!
7893

7994
// setup the menus
80-
var buttonsConfig = require('./config/buttons.json');
95+
96+
if (preferences.hardwaretype && preferences.hardwaretype == "radiofruit") {
97+
var buttonsConfig = require('./config/buttons-radiofruit.json');
98+
} else {
99+
var buttonsConfig = require('./config/buttons-explorerhat.json');
100+
}
101+
81102
var menuConfig = {
82103
menuFile: process.cwd() + path.sep + './config/menus/menu.json', // file path for the menu definition
83104
onChange: showMenu, // method to call when menu changes
@@ -99,8 +120,15 @@ hidMenu
99120
.on('showbigBGstatus', function () {
100121
bigBGStatus(display, openapsDir);
101122
})
123+
.on('showRadiofruitStatus', function () {
124+
radiofruitStatus(display, openapsDir);
125+
})
102126
.on('showlogo', function () {
103-
displayImage('./static/unicorn.png');
127+
if (preferences.hardwaretype && preferences.hardwaretype == "radiofruit") {
128+
displayImage('./static/unicorn_128x32.png');
129+
} else {
130+
displayImage('./static/unicorn_128x64.png');
131+
}
104132
})
105133
.on('showvoltage', function () {
106134
voltage()

package.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@
99
"author": "",
1010
"license": "MIT",
1111
"dependencies": {
12-
"extend": "^3.0.1",
13-
"i2c-bus": "^1.2.2",
14-
"menube": "^1.0.3",
15-
"oled-font-5x7": "^1.0.0",
16-
"oled-i2c-bus": "git+https://github.com/bnielsen1965/oled-i2c-bus.git",
12+
"extend": "^3.0.2",
13+
"i2c-bus": "^4.0.10",
14+
"menube": "^1.0.4",
15+
"oled-font-5x7": "^1.0.3",
16+
"oled-i2c-bus": "^1.0.11",
1717
"pngparse": "^2.0.1",
18-
"node-pi-buttons": "git+https://github.com/bnielsen1965/node-pi-buttons.git",
19-
"rpi-gpio": "^0.9.1"
18+
"node-pi-buttons": "^1.0.1",
19+
"rpi-gpio": "^2.1.3"
2020
}
2121
}

scripts/getvoltage.sh

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
command -v socat >/dev/null 2>&1 || { echo >&2 "I require socat but it's not installed. Aborting."; exit 1; }
44

5-
[[ $RESPONSE == "{}" ]] && unset RESPONSE
65
RESPONSE=`echo '{"command":"read_voltage"}' | socat -,ignoreeof ~/src/openaps-menu/socket-server.sock | sed -n 's/.*"response":\([^}]*\)}/\1/p'`
7-
[[ $RESPONSE = *[![:space:]]* ]] && echo $RESPONSE
6+
[[ $RESPONSE == "{}" ]] && unset RESPONSE
7+
[[ $RESPONSE = *[![:space:]]* ]] && echo $RESPONSE || echo '{"batteryVoltage":3340,"battery":99}'
8+
# the OR at the end of the above line uploads a fake voltage (3340) and percentage (99), to work around a problem with nighscout crashing when receiving a null value
89
#./getvoltage.sh | sed -n 's/.*"response":\([^}]*\)}/\1/p'

scripts/status-radiofruit.js

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
var fs = require('fs');
2+
var font = require('oled-font-5x7');
3+
4+
// Rounds value to 'digits' decimal places
5+
function round(value, digits)
6+
{
7+
if (! digits) { digits = 0; }
8+
var scale = Math.pow(10, digits);
9+
return Math.round(value * scale) / scale;
10+
}
11+
12+
function convert_bg(value, profile)
13+
{
14+
if (profile != null && profile.out_units == "mmol/L")
15+
{
16+
return round(value / 18, 1).toFixed(1);
17+
}
18+
else
19+
{
20+
return Math.round(value);
21+
}
22+
}
23+
24+
function stripLeadingZero(value)
25+
{
26+
var re = /^(-)?0+(?=[\.\d])/;
27+
return value.toString().replace( re, '$1');
28+
}
29+
30+
module.exports = radiofruitStatus;
31+
32+
//
33+
//Start of status display function
34+
//
35+
36+
function radiofruitStatus(display, openapsDir) {
37+
38+
display.oled.clearDisplay(true); //clear display buffer
39+
40+
//Parse all the .json files we need
41+
try {
42+
var profile = JSON.parse(fs.readFileSync(openapsDir+"/settings/profile.json"));
43+
} catch (e) {
44+
console.error("Status screen display error: could not parse profile.json: ", e);
45+
}
46+
try {
47+
var status = JSON.parse(fs.readFileSync(openapsDir+"/monitor/status.json"));
48+
} catch (e) {
49+
console.error("Status screen display error: could not parse status.json: ", e);
50+
}
51+
try {
52+
var suggested = JSON.parse(fs.readFileSync(openapsDir+"/enact/suggested.json"));
53+
} catch (e) {
54+
console.error("Status screen display error: could not parse suggested.json: ", e);
55+
}
56+
try {
57+
var bg = JSON.parse(fs.readFileSync(openapsDir+"/monitor/glucose.json"));
58+
} catch (e) {
59+
console.error("Status screen display error: could not parse glucose.json: ", e);
60+
}
61+
try {
62+
var temp = JSON.parse(fs.readFileSync(openapsDir+"/monitor/last_temp_basal.json"));
63+
var statusStats = fs.statSync(openapsDir+"/monitor/last_temp_basal.json");
64+
} catch (e) {
65+
console.error("Status screen display error: could not parse last_temp_basal.json: ", e);
66+
}
67+
try {
68+
var iob = JSON.parse(fs.readFileSync(openapsDir+"/monitor/iob.json"));
69+
} catch (e) {
70+
console.error("Status screen display error: could not parse iob.json: ", e);
71+
}
72+
try {
73+
var cob = JSON.parse(fs.readFileSync(openapsDir+"/monitor/meal.json"));
74+
} catch (e) {
75+
console.error("Status screen display error: could not parse meal.json: ", e);
76+
}
77+
try {
78+
var pumpbattery = JSON.parse(fs.readFileSync(openapsDir+"/monitor/battery.json"));
79+
} catch (e) {
80+
console.error("Status screen display error: could not parse battery.json: ", e);
81+
}
82+
83+
//display warning messages
84+
if (status && suggested && pumpbattery) {
85+
var notLoopingReason = suggested.reason;
86+
display.oled.setCursor(0,16);
87+
if (pumpbattery.voltage <= 1.25) {
88+
display.oled.writeString(font, 1, "LOW PUMP BATT.", 1, false, 0, false);
89+
yOffset = 3;
90+
}
91+
else if (status.suspended == true) {
92+
display.oled.writeString(font, 1, "PUMP SUSPENDED", 1, false, 0, false);
93+
yOffset = 3;
94+
}
95+
else if (status.bolusing == true) {
96+
display.oled.writeString(font, 1, "PUMP BOLUSING", 1, false, 0, false);
97+
yOffset = 3;
98+
}
99+
else if (notLoopingReason.includes("CGM is calibrating")) {
100+
display.oled.writeString(font, 1, "CGM calib./???/noisy", 1, false, 0, false);
101+
yOffset = 3;
102+
}
103+
else if (notLoopingReason.includes("CGM data is unchanged")) {
104+
display.oled.writeString(font, 1, "CGM data unchanged", 1, false, 0, false);
105+
yOffset = 3;
106+
}
107+
else if (notLoopingReason.includes("BG data is too old")) {
108+
display.oled.writeString(font, 1, "BG data too old", 1, false, 0, false);
109+
yOffset = 3;
110+
}
111+
else if (notLoopingReason.includes("currenttemp rate")) {
112+
display.oled.writeString(font, 1, "Temp. mismatch", 1, false, 0, false);
113+
yOffset = 3;
114+
}
115+
else if (suggested.carbsReq) {
116+
display.oled.writeString(font, 1, "Carbs Requiredd: "+suggested.carbsReq+'g', 1, false, 0, false);
117+
yOffset = 3;
118+
}
119+
//add more on-screen warnings/messages, maybe some special ones for xdrip-js users?
120+
}
121+
122+
//calculate timeago for BG
123+
var startDate = new Date(bg[0].date);
124+
var endDate = new Date();
125+
var minutes = Math.round(( (endDate.getTime() - startDate.getTime()) / 1000) / 60);
126+
if (bg[0].delta) {
127+
var delta = Math.round(bg[0].delta);
128+
} else if (bg[1] && bg[0].date - bg[1].date > 200000 ) {
129+
var delta = Math.round(bg[0].glucose - bg[1].glucose);
130+
} else if (bg[2] && bg[0].date - bg[2].date > 200000 ) {
131+
var delta = Math.round(bg[0].glucose - bg[2].glucose);
132+
} else if (bg[3] && bg[0].date - bg[3].date > 200000 ) {
133+
var delta = Math.round(bg[0].glucose - bg[3].glucose);
134+
} else {
135+
var delta = 0;
136+
}
137+
//display BG number and timeago, add plus sign if delta is positive
138+
display.oled.setCursor(0,24);
139+
if (delta >= 0) {
140+
display.oled.writeString(font, 1, "BG:"+convert_bg(bg[0].glucose, profile)+"+"+stripLeadingZero(convert_bg(delta, profile))+" "+minutes+"m", 1, false, 0, false);
141+
} else {
142+
display.oled.writeString(font, 1, "BG:"+convert_bg(bg[0].glucose, profile)+""+stripLeadingZero(convert_bg(delta, profile))+" "+minutes+"m", 1, false, 0, false);
143+
}
144+
145+
//display current temp basal and how long ago it was set, on the first line of the screen
146+
if (statusStats && temp) {
147+
startDate = new Date(statusStats.mtime);
148+
endDate = new Date();
149+
var minutesAgo = Math.round(( (endDate.getTime() - startDate.getTime()) / 1000) / 60);
150+
//display current temp basal
151+
display.oled.setCursor(0,0);
152+
var tempRate = Math.round(temp.rate*10)/10;
153+
display.oled.writeString(font, 1, "TB: "+temp.duration+'m '+tempRate+'U/h '+'('+minutesAgo+'m ago)', 1, false, 0, false);
154+
}
155+
156+
//display current COB and IOB, on the second line of the screen
157+
if (iob && cob) {
158+
display.oled.setCursor(0,8);
159+
display.oled.writeString(font, 1, "COB: "+cob.mealCOB+"g IOB: "+iob[0].iob+'U', 1, false, 0, false);
160+
}
161+
162+
//render clock
163+
var clockDate = new Date();
164+
var clockHour = clockDate.getHours();
165+
clockHour = (clockHour < 10 ? "0" : "") + clockHour;
166+
var clockMin = clockDate.getMinutes();
167+
clockMin = (clockMin < 10 ? "0" : "") + clockMin;
168+
display.oled.setCursor(97, 24);
169+
display.oled.writeString(font, 1, clockHour+":"+clockMin, 1, false, 0, false);
170+
171+
display.oled.dimDisplay(true); //dim the display
172+
display.oled.update(); //write buffer to the screen
173+
174+
fs.readFile(openapsDir+"/preferences.json", function (err, data) {
175+
if (err) throw err;
176+
preferences = JSON.parse(data);
177+
if (preferences.wearOLEDevenly && preferences.wearOLEDevenly.includes("off")) {
178+
display.oled.invertDisplay(false);
179+
}
180+
else if (preferences.wearOLEDevenly && preferences.wearOLEDevenly.includes("nightandday") && (clockHour >= 20 || clockHour <= 8)) {
181+
display.oled.invertDisplay(false);
182+
}
183+
else if (preferences.wearOLEDevenly && preferences.wearOLEDevenly.includes("nightandday") && (clockHour <= 20 && clockHour >= 8)) {
184+
display.oled.invertDisplay(true);
185+
}
186+
else {
187+
display.oled.invertDisplay((endDate % 2 == 1));
188+
}
189+
});
190+
191+
//
192+
}//End of status display function
193+
//

0 commit comments

Comments
 (0)