summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDerf Null <derf@finalrewind.org>2023-04-22 19:57:22 +0200
committerDerf Null <derf@finalrewind.org>2023-04-22 19:57:22 +0200
commit0d295cc6a413ef6804134030a43e6c1c2467499c (patch)
treede284c2c7bdec64dc9d0a130ced08d72d7969f62
initial commit
-rw-r--r--.gitmodules6
-rw-r--r--case/scd4x-ssd1306.scad172
m---------ext/scd4x0
m---------ext/ssd13060
l---------src/framebuffer.lua1
-rw-r--r--src/init.lua177
l---------src/scd4x.lua1
l---------src/ssd1306.lua1
l---------src/terminus16.lua1
9 files changed, 359 insertions, 0 deletions
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..c402270
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,6 @@
+[submodule "ext/scd4x"]
+ path = ext/scd4x
+ url = https://github.com/derf/esp8266-nodemcu-scd4x.git
+[submodule "ext/ssd1306"]
+ path = ext/ssd1306
+ url = https://github.com/derf/esp8266-nodemcu-ssd1306.git
diff --git a/case/scd4x-ssd1306.scad b/case/scd4x-ssd1306.scad
new file mode 100644
index 0000000..081711c
--- /dev/null
+++ b/case/scd4x-ssd1306.scad
@@ -0,0 +1,172 @@
+height = 22;
+taster_y = 21;
+taster_x = 26;
+
+m3 = 1.55;
+nut = [5.4, 2.4, 5.4];
+nutbox = [8, 8, 8];
+m3_zo = 4;
+
+module battery(top=false) {
+ liion = [21.8, 81, height];
+ wall1 = [2, 2, 2];
+ wall2 = [2, 2, 0];
+ if (top) {
+ translate(-wall2) cube([liion.x, liion.y, 0] + wall1 + wall2);
+ } else {
+ difference() {
+ translate(-wall1) cube(liion + wall1 + wall2);
+ cube(liion);
+ translate([liion.x, 0, 0]) cube([wall2.x, 4, height]);
+ translate([liion.x, liion.y-15, 0]) cube([wall2.x, 15, height]);
+ }
+ }
+}
+
+module board(top=false) {
+ $fn=60;
+ pcb = [59, 71, height];
+ wall1 = [0, 2, 2];
+ wall2 = [2, 2, 0];
+ scdwall = [27, 2, 15];
+ scdwall_o = [0, pcb.y - scdwall.y - 25, -scdwall.z];
+ espwall = [pcb.x, 2, 12];
+ espwall_o = [0, pcb.y - espwall.y - 31, -espwall.z];
+ disp = [23.5, 13.5, wall1.z];
+ disp_o = [32, pcb.y - disp.y - 8, 0];
+ disp_wx = [disp.x + 2, 1, 5.5];
+ disp_wy = [1, disp.y + 2, 5.5];
+ disp_wx1o = [disp_o.x - 1, disp_o.y - 1, -disp_wx.z];
+ disp_wx2o = [disp_o.x - 1, disp_o.y + disp.y, -disp_wx.z];
+ disp_wy1o = disp_wx1o;
+ disp_wy2o = [disp_o.x + disp.x, disp_o.y - 1, -disp_wy.z];
+ extra_x = 8;
+ extra_y = 8;
+ extra_floor = [extra_x, extra_y, wall1.z];
+ m3_1_o = [4, -wall1.y, pcb.z-m3_zo];
+ m3_2_o = [4, pcb.y + wall2.y + 10, pcb.z-m3_zo];
+ if (top) {
+ difference() {
+ translate(-wall2) cube([pcb.x, pcb.y, 0] + wall1 + wall2 + [wall2.x, 0, 0]);
+ for (x = [0:2]) {
+ for (y = [0:2]) {
+ translate([5 + 8*x, pcb.y - wall2.y - 2 - 8*y, 0]) cylinder(h=wall2.y, r=2);
+ }
+ }
+ for (x = [0:6]) {
+ for (y = [4:7]) {
+ translate([5 + 8*x, pcb.y - wall2.y - 2 - 8*y, 0]) cylinder(h=wall2.y, r=1.5);
+ }
+ }
+ translate(disp_o - [5, 5, 0]) cube(disp + [10, 10, 0]);
+ //translate([m3_1_o.x - nut.x/2, 3, 0]) cube(nut);
+ }
+ for (i = [0:0.1:6]) {
+ intersection() {
+ union() {
+ translate(disp_wx1o - [i, i, 0]) cube(disp_wx + [2*i, 0, 0]);
+ translate(disp_wx2o - [i, -i, 0]) cube(disp_wx + [2*i, 0, 0]);
+ translate(disp_wy1o - [i, i, 0]) cube(disp_wy + [0, 2*i, 0]);
+ translate(disp_wy2o - [-i, i, 0]) cube(disp_wy + [0, 2*i, 0]);
+ }
+ translate(disp_o - [i+2, i+2, 6-i]) cube(disp + [2*i+8, 2*i+8, -0.8]);
+ }
+ }
+ translate([0, pcb.y + wall2.y, 0]) cube([extra_x + wall2.x, extra_y + wall1.y, wall1.z]);
+ translate(scdwall_o) cube(scdwall);
+ translate(espwall_o) cube(espwall);
+ difference() {
+ translate([m3_1_o.x - nutbox.x/2, 0.1, -nutbox.z]) cube(nutbox);
+ translate([m3_1_o.x - nut.x/2, 3, -m3_zo -nut.z/2]) cube(nut + [10, 0, 0]);
+ #translate([m3_1_o.x, 0, -m3_zo]) rotate([-90, 0, 0]) cylinder(h=10, r=m3);
+ }
+ difference() {
+ translate([m3_2_o.x - nutbox.x/2, pcb.y + wall2.y + extra_y - nutbox.y - 0.1, -nutbox.z]) cube(nutbox);
+ translate([m3_2_o.x - nut.x/2, pcb.y + wall2.y + extra_y - 5.1, -m3_zo -nut.z/2]) cube(nut + [10, 0, 0]);
+ translate([m3_2_o.x, pcb.y + extra_y - nutbox.y - 0.1, -m3_zo]) rotate([-90, 0, 0]) cylinder(h=10, r=m3);
+ }
+ } else {
+ difference() {
+ translate(-wall1) cube(pcb + wall1 + wall2);
+ cube(pcb);
+ translate([0, pcb.y, 0]) cube([extra_x, wall2.y, height]);
+ translate([pcb.x, 15, 0]) cube([wall2.x, pcb.y-30, height]);
+ translate([pcb.x, 30, height/2]) cube([wall2.x, pcb.y-30, height/2]);
+ translate([8, -wall1.y, 4]) cube([15, wall1.y, height-4]);
+ translate(m3_1_o) rotate([-90, 0, 0]) cylinder(h=5, r=m3);
+ translate(m3_1_o) rotate([-90, 0, 0]) cylinder(h=1.8, r1=1.8*m3, r2=m3);
+ for (x = [0:1]) {
+ for (z = [0:1]) {
+ translate([extra_x + 2*wall2.x + 8*x, pcb.y + wall2.y, 10 + 8*z]) rotate([90, 0, 0]) cylinder(h=wall2.y, r=2);
+ }
+ }
+ }
+ translate([0, pcb.y + wall2.y, -wall1.z]) cube(extra_floor);
+ difference() {
+ union() {
+ translate([0, pcb.y + wall2.y + extra_y, -wall1.z]) cube([extra_x, wall2.y, wall1.z+height]);
+ translate([extra_x, pcb.y + wall2.y, -wall1.z]) cube([wall2.x, extra_y+wall2.y, wall1.z+height]);
+ }
+ translate(m3_2_o) rotate([90, 0, 0]) cylinder(h=10, r=m3);
+ translate(m3_2_o) rotate([90, 0, 0]) cylinder(h=1.8, r1=1.8*m3, r2=m3);
+ }
+ }
+}
+
+module taster(top=false) {
+ $fn=60;
+ taster_y = 21;
+ dim = [26, 71, height];
+ wall1 = [0, 2, 2];
+ wall2 = [1, 2, 0];
+ posl = [dim.x - 0.3, 2, 10];
+ posl_o = [0.15, 0.15, -posl.z];
+ post = [dim.x - 0.3, 2, 10];
+ post_o = [0.15, dim.y - post.y - 0.15, -post.z];
+ m3_1_o = [dim.x/2, -wall1.y, dim.z-m3_zo];
+ m3_2_o = [dim.x/2, dim.y+wall1.y, dim.z-m3_zo];
+ if (top) {
+ translate(-wall2) cube([dim.x, dim.y, 0] + wall1 + wall2 + [wall2.x, 0, 0]);
+ difference() {
+ union() {
+ translate([m3_1_o.x - nutbox.x/2, 0.1, -nutbox.z]) cube(nutbox);
+ translate(posl_o) cube(posl);
+ }
+ translate([m3_1_o.x - nut.x/2, 3, -m3_zo -nut.z/2]) cube(nut + [10, 0, 0]);
+ #translate([m3_1_o.x, 0, -m3_zo]) rotate([-90, 0, 0]) cylinder(h=10, r=m3);
+ }
+ difference() {
+ union() {
+ translate(post_o) cube(post);
+ translate([m3_2_o.x - nutbox.x/2, dim.y - nutbox.y - 0.1, -nutbox.z]) cube(nutbox);
+ }
+ translate([m3_2_o.x - nut.x/2, dim.y - 5.1, -m3_zo -nut.z/2]) cube(nut + [10, 0, 0]);
+ translate([m3_2_o.x, dim.y - nutbox.y - 0.1, -m3_zo]) rotate([-90, 0, 0]) cylinder(h=10, r=m3);
+ }
+ } else {
+ difference() {
+ translate(-wall1) cube(dim + wall1 + wall2);
+ cube(dim);
+ translate([dim.x, 20, 0]) cube([wall2.x, taster_y, height]);
+ translate(m3_1_o) rotate([-90, 0, 0]) cylinder(h=5, r=m3);
+ translate(m3_1_o) rotate([-90, 0, 0]) cylinder(h=1.8, r1=1.8*m3, r2=m3);
+ translate(m3_2_o) rotate([90, 0, 0]) cylinder(h=5, r=m3);
+ translate(m3_2_o) rotate([90, 0, 0]) cylinder(h=1.8, r1=1.8*m3, r2=m3);
+ }
+ }
+}
+
+module bottom() {
+ battery();
+ translate([23.8, 0, 0]) board();
+ translate([23.8+59+2, 0, 0]) taster();
+}
+
+module top() {
+ battery(top=true);
+ translate([23.8, 0, 0]) board(top=true);
+ translate([23.8+59+2, 0, 0]) taster(top=true);
+}
+
+bottom();
+translate([0, 0, 22+0]) top(); \ No newline at end of file
diff --git a/ext/scd4x b/ext/scd4x
new file mode 160000
+Subproject 6abca13a48be662b53ec98377a9e2edbb9aa8c9
diff --git a/ext/ssd1306 b/ext/ssd1306
new file mode 160000
+Subproject 888ad2d273ce8353dc90b3bdc99021a0fefa635
diff --git a/src/framebuffer.lua b/src/framebuffer.lua
new file mode 120000
index 0000000..ba71daf
--- /dev/null
+++ b/src/framebuffer.lua
@@ -0,0 +1 @@
+../ext/ssd1306/framebuffer.lua \ No newline at end of file
diff --git a/src/init.lua b/src/init.lua
new file mode 100644
index 0000000..2286d4e
--- /dev/null
+++ b/src/init.lua
@@ -0,0 +1,177 @@
+station_cfg = {}
+
+chip_id = string.format("%06X", node.chipid())
+device_id = "esp8266_" .. chip_id
+
+dofile("config.lua")
+
+i2c.setup(0, 1, 2, i2c.SLOW)
+ssd1306 = require("ssd1306")
+fn = require("terminus16")
+fb = require("framebuffer")
+scd4x = require("scd4x")
+
+ledpin = 4
+gpio.mode(ledpin, gpio.OUTPUT)
+gpio.write(ledpin, 0)
+
+ssd1306.init(128, 64)
+ssd1306.contrast(255)
+fb.init(128, 64)
+
+timestamp = 0
+old_timestamp = 0
+no_wifi_count = 0
+publish_count = 0
+message_queue = {}
+
+-- cal 2023-01-06
+-- 4.2V -> "4.36V" (raw ~ 948)
+-- 4.0V -> "4.16V" (raw ~ 905)
+-- 3.8V -> "3.95V" (raw ~ 859)
+-- 3.6V -> "3.74V" (raw ~ 814)
+function get_battery_mv()
+ return adc.read(0) * 461 / 104
+end
+
+function get_battery_percent(bat_mv)
+ if bat_mv > 4160 then
+ return 100
+ end
+ if bat_mv < 3360 then
+ return 0
+ end
+ return (bat_mv - 3360) / 8
+end
+
+function get_time()
+ publishing_http = true
+ http.get("http://arclight:1234/", nil, function(status, body, headers)
+ publishing_http = false
+ if timestamp < 604800 then
+ old_timestamp = timestamp
+ end
+ timestamp = tonumber(body)
+ end)
+end
+
+function connect_wifi()
+ print("WiFi MAC: " .. wifi.sta.getmac())
+ print("Connecting to ESSID " .. station_cfg.ssid)
+ wifi.eventmon.register(wifi.eventmon.STA_GOT_IP, wifi_connected)
+ wifi.eventmon.register(wifi.eventmon.STA_DHCP_TIMEOUT, wifi_err)
+ wifi.eventmon.register(wifi.eventmon.STA_DISCONNECTED, wifi_err)
+ wifi.setmode(wifi.STATION)
+ wifi.sta.config(station_cfg)
+ wifi.sta.connect()
+end
+
+function scd4x_start()
+ if scd4x.start() then
+ gpio.write(ledpin, 1)
+ else
+ print("SCD4x error")
+ end
+ local measure_t = tmr.create()
+ measure_t:register(5 * 1000, tmr.ALARM_AUTO, measure)
+ measure_t:start()
+end
+
+function measure()
+ timestamp = timestamp + 5
+ fb.init(128, 64)
+ local co2, raw_temp, raw_humi = scd4x.read()
+ local bat_mv = get_battery_mv()
+ local bat_p = get_battery_percent(bat_mv)
+ local line1 = ""
+ local line2 = ""
+ if co2 == nil then
+ line1 = "SCD4x error"
+ fb.print(fn, line1)
+ ssd1306.show(fb.buf)
+ return
+ end
+ line1 = string.format("%8d ppm\n\n", co2)
+ line2 = string.format("%8d.%d c\n%8d.%d %%", raw_temp/65536 - 45, (raw_temp%65536)/6554, raw_humi/65536, (raw_humi%65536)/6554)
+ fb.print(fn, line1)
+ fb.print(fn, line2)
+ fb.draw_battery_8(114, 0, bat_p)
+ if have_wifi then
+ fb.x = 90
+ fb.print(fn, string.format("%02d:%02d", (timestamp / 3600) % 24, (timestamp / 60) % 60))
+ elseif no_wifi_count < 120 then
+ no_wifi_count = no_wifi_count + 1
+ else
+ table.insert(message_queue, {timestamp, co2, raw_temp, raw_humi})
+ fb.x = 100
+ fb.print(fn, string.format("%d", table.getn(message_queue)))
+ no_wifi_count = 0
+ connect_wifi()
+ end
+ ssd1306.show(fb.buf)
+ fb.init(128, 64)
+ publish_count = publish_count + 1
+ if have_wifi and influx_url and publish_count >= 4 and not publishing_http then
+ publish_count = 0
+ gpio.write(ledpin, 0)
+ publish_influx(co2, raw_temp, raw_humi, bat_mv)
+ elseif have_wifi and influx_url and not publishing_http and timestamp > 604800 then
+ for i, v in ipairs(message_queue) do
+ if v[1] < 604800 then
+ v[1] = timestamp - (old_timestamp - v[1])
+ end
+ end
+ --print(timestamp)
+ empty_queue(message_queue)
+ else
+ collectgarbage()
+ end
+end
+
+function publish_influx(co2, raw_temp, raw_humi, bat_mv)
+ publishing_http = true
+ http.post(influx_url, influx_header, string.format("scd4x%s co2_ppm=%d,temperature_celsius=%d.%d,humidity_relpercent=%d.%d", influx_attr, co2, raw_temp/65536 - 45, (raw_temp%65536)/6554, raw_humi/65536, (raw_humi%65536)/6554), function(code, data)
+ http.post(influx_url, influx_header, string.format("esp8266%s battery_mv=%d", influx_attr, bat_mv), function(code, data)
+ publishing_http = false
+ gpio.write(ledpin, 1)
+ get_time()
+ collectgarbage()
+ end)
+ end)
+end
+
+function empty_queue(q)
+ local n = table.getn(q)
+ if n > 0 then
+ publishing_http = true
+ gpio.write(ledpin, 0)
+ local ts = q[n][1]
+ local co2 = q[n][2]
+ local t = q[n][3]
+ local h = q[n][4]
+ table.remove(q)
+ --print(influx_url .. '&precision=s')
+ --print(string.format("scd4x%s co2_ppm=%d,temperature_celsius=%d.%d,humidity_relpercent=%d.%d %d", influx_attr, co2, t/65536 - 45, (t%65536)/6554, h/65536, (h%65536)/6554, ts))
+ http.post(influx_url .. '&precision=s', influx_header, string.format("scd4x%s co2_ppm=%d,temperature_celsius=%d.%d,humidity_relpercent=%d.%d %d", influx_attr, co2, t/65536 - 45, (t%65536)/6554, h/65536, (h%65536)/6554, ts), function(code, data)
+ --print('Q ' .. n .. ' returned ' .. code .. ' ' .. data)
+ empty_queue(q)
+ end)
+ else
+ publishing_http = false
+ gpio.write(ledpin, 1)
+ end
+end
+
+local init_t = tmr.create()
+init_t:register(1 * 1000, tmr.ALARM_SINGLE, scd4x_start)
+init_t:start()
+
+function wifi_connected()
+ have_wifi = true
+end
+
+function wifi_err()
+ have_wifi = false
+end
+
+connect_wifi()
diff --git a/src/scd4x.lua b/src/scd4x.lua
new file mode 120000
index 0000000..6d93ddd
--- /dev/null
+++ b/src/scd4x.lua
@@ -0,0 +1 @@
+../ext/scd4x/scd4x.lua \ No newline at end of file
diff --git a/src/ssd1306.lua b/src/ssd1306.lua
new file mode 120000
index 0000000..ed7ee52
--- /dev/null
+++ b/src/ssd1306.lua
@@ -0,0 +1 @@
+../ext/ssd1306/ssd1306.lua \ No newline at end of file
diff --git a/src/terminus16.lua b/src/terminus16.lua
new file mode 120000
index 0000000..1ee383e
--- /dev/null
+++ b/src/terminus16.lua
@@ -0,0 +1 @@
+../ext/ssd1306/terminus16.lua \ No newline at end of file