diff --git a/modules/ADS1015.js b/modules/ADS1015.js index da5d330..e428f8f 100644 --- a/modules/ADS1015.js +++ b/modules/ADS1015.js @@ -7,6 +7,7 @@ const i2c = require('i2c-bus'); // -> https://github.com/fivdi/i2c-bus const SH1107 = require('./SH1107.js'); const IS31FL3731_RGB = require('./IS31FL3731_RGB.js'); const IS31FL3731_WHITE = require('./IS31FL3731_WHITE.js'); +const HT16K33 = require('./HT16K33.js'); module.exports = { Identify: Identify, @@ -250,6 +251,10 @@ function Display(refreshAll) } } + // ==================== + + if (HT16K33.IsAvailable()) HT16K33.Display(""); // PLACEHOLDER + // ==================== if (refreshAll) Log(); diff --git a/modules/ADT7410.js b/modules/ADT7410.js index fa75faa..d8d2c46 100644 --- a/modules/ADT7410.js +++ b/modules/ADT7410.js @@ -7,6 +7,7 @@ const i2c = require('i2c-bus'); // -> https://github.com/fivdi/i2c-bus const SH1107 = require('./SH1107.js'); const IS31FL3731_RGB = require('./IS31FL3731_RGB.js'); const IS31FL3731_WHITE = require('./IS31FL3731_WHITE.js'); +const HT16K33 = require('./HT16K33.js'); module.exports = { Identify: Identify, @@ -150,7 +151,15 @@ function Display(refreshAll) if (IS31FL3731_WHITE.IsAvailable()) { IS31FL3731_WHITE.DrawString(Math.round(data[0]).toString()); - } + } + + // ==================== + + if (HT16K33.IsAvailable()) + { + var temperature = data[0].toFixed(1) + 'C'; + HT16K33.Display(temperature); + } // ==================== diff --git a/modules/ADXL343.js b/modules/ADXL343.js index f052e37..a204223 100644 --- a/modules/ADXL343.js +++ b/modules/ADXL343.js @@ -7,6 +7,7 @@ const i2c = require('i2c-bus'); // -> https://github.com/fivdi/i2c-bus const SH1107 = require('./SH1107.js'); const IS31FL3731_RGB = require('./IS31FL3731_RGB.js'); const IS31FL3731_WHITE = require('./IS31FL3731_WHITE.js'); +const HT16K33 = require('./HT16K33.js'); module.exports = { Identify: Identify, @@ -335,7 +336,47 @@ function Display(refreshAll) if (data [8] > 0) img[38] = 180; IS31FL3731_WHITE.Display(img); - } + } + + // ==================== + + if (HT16K33.IsAvailable()) + { + // We only have 4 characters to play with (for now at least, without scrolling) + // so let's divide the roll/pitch degrees by 10 and present them in truncated "#R#P" format... + + var rollpitch = ''; + + var roll; + if (data[6] < 0) + { + rollpitch += '-'; + roll = -Math.round(data[6]/10); + } + else + { + rollpitch += '+'; + roll = Math.round(data[6]/10); + } + if (roll > 9) rollpitch += 'R'; + else rollpitch += roll.toString(); + + var pitch; + if (data[7] < 0) + { + rollpitch += '-'; + pitch = -Math.round(data[7]/10); + } + else + { + rollpitch += '+'; + pitch = Math.round(data[7]/10); + } + if (pitch > 9) rollpitch += 'P'; + else rollpitch += pitch.toString(); + + HT16K33.Display(rollpitch); + } // ==================== diff --git a/modules/AS7262.js b/modules/AS7262.js new file mode 100644 index 0000000..b436c8e --- /dev/null +++ b/modules/AS7262.js @@ -0,0 +1,377 @@ +// ============================================================================================ +// --- BREAKOUT GARDENER :: MODULES :: AS7262 --- +// (c) 2018-2019 Karl-Henrik Henriksson - breakouts*xoblite.net - http://breakouts.xoblite.net/ +// ============================================================================================ + +const i2c = require('i2c-bus'); // -> https://github.com/fivdi/i2c-bus +const SH1107 = require('./SH1107.js'); +const IS31FL3731_RGB = require('./IS31FL3731_RGB.js'); +const IS31FL3731_WHITE = require('./IS31FL3731_WHITE.js'); +const HT16K33 = require('./HT16K33.js'); + +const { performance } = require('perf_hooks'); +function Wait(msecs) { const start = performance.now(); while(performance.now() - start < msecs); } // Helper function + +module.exports = { + Identify: Identify, + IsAvailable: IsAvailable, + Start: Start, + Stop: Stop, + Get: Get, + Log: Log, + Display: Display +}; + +// ================================================================================ + +// ---------------------------------------------------------------------------------------- +// === AMS AS7262 6-channel Spectral Sensor === +// * Product Page -> https://ams.com/AS7262 +// * Datasheet -> https://ams.com/documents/20143/36005/AS7262_DS000486_2-00.pdf +// ---------------------------------------------------------------------------------------- + +const AS7262_STATUS_REGISTER = 0x00; +const AS7262_WRITE_REGISTER = 0x01; +const AS7262_READ_REGISTER = 0x02; +const AS7262_STATUS_RX_VALID = 0x01; +const AS7262_STATUS_TX_VALID = 0x02; + +var I2C_BUS = 0, I2C_ADDRESS_AS7262 = 0; +var enableLEDs = false; +var data = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]; +var alternatingDisplay = false; +var outputLogs = false, showDebug = false; + +// ================================================================================ + +// # Below: Helper functions to read/write to the virtual registers of the device # + +function CheckTXready(bus, address) +{ + while (true) + { + var status = (bus.readByteSync(address, AS7262_STATUS_REGISTER) & AS7262_STATUS_TX_VALID); + // if (showDebug) console.log(" --- Checking TX_VALID... -> %s", status); + if (status == 0) break; + else Wait(10); + } +} + +function CheckRXready(bus, address) +{ + while (true) + { + var status = (bus.readByteSync(address, AS7262_STATUS_REGISTER) & AS7262_STATUS_RX_VALID); + // if (showDebug) console.log(" --- Checking RX_VALID... -> %s", status); + if (status != 0) break; + else Wait(10); + } +} + +function GetVirtualRegister(bus, address, virtualRegister) +{ + CheckTXready(bus, address); // Check that the device is ready... + bus.writeByteSync(address, AS7262_WRITE_REGISTER, virtualRegister); // Select the virtual register to read... + CheckRXready(bus, address); // Wait for the requested data to become available... + return bus.readByteSync(address, AS7262_READ_REGISTER); // Read the requested data... +} + +function SetVirtualRegister(bus, address, virtualRegister, value) +{ + CheckTXready(bus, address); // Check that the device is ready... + bus.writeByteSync(address, AS7262_WRITE_REGISTER, (virtualRegister | 0x80)); // Select the virtual register to write, and indicate a pending write to it by setting the "write bit" flag... (0x80 i.e. bit D7) + CheckTXready(bus, address); // Wait for the device again... + bus.writeByteSync(address, AS7262_WRITE_REGISTER, value); // Write the supplied data... +} + +function GetVirtRegMultipleValues(bus, address, virtualRegister, buffer) +{ + for (var n=0; n<=buffer.length; n++) + { + buffer[n] = GetVirtualRegister(bus, address, virtualRegister+n); + } +} + +// ================================================================================ + +function Identify(bus, address) +{ + if (I2C_ADDRESS_AS7262 > 0) return false; + + // Identify using the device ID (0x40) of the AS7262 device... + var deviceID = GetVirtualRegister(bus, address, 0x00); + if (deviceID == 0x40) + { + I2C_BUS = bus; + I2C_ADDRESS_AS7262 = address; + return true; + } + else return false; +} + +// ==================== + +function IsAvailable() +{ + if (I2C_ADDRESS_AS7262) return true; + else return false; +} + +// ==================== + +function Start(leds, logs, debug) +{ + if (logs) outputLogs = true; + if (debug) showDebug = true; + + if (leds) enableLEDs = true; + + // Soft reset the device... (just in case?) + // SetVirtualRegister(I2C_BUS, I2C_ADDRESS_AS7262, 0x04, 0x80); + // Wait(1000); + + // Configure the device... + // ### ANYTHING TO BE ADDED HERE? ### +} + +function Stop() { return; } + +// ==================== + +function Get() +{ + // Violet @ 450 nm + // Blue @ 500 nm + // Green @ 550 nm + // Yellow @ 570 nm + // Orange @ 600 nm + // Red @ 650 nm + + if (enableLEDs) + { + // Turn on the illumnation LED @ 12.5 mA... (weakest setting but quite bright anyway) + SetVirtualRegister(I2C_BUS, I2C_ADDRESS_AS7262, 0x07, 0b00001000); + Wait(10); + } + + // Trigger a one-shot measurement (BANK mode 3) with 64x gain... + SetVirtualRegister(I2C_BUS, I2C_ADDRESS_AS7262, 0x04, 0b00111100); + Wait(100); // -> Just in case?! + + if (enableLEDs) + { + // Turn off the illumnation LED... + SetVirtualRegister(I2C_BUS, I2C_ADDRESS_AS7262, 0x07, 0b00000000); + } + + // ==================== + + // Read the VBGYOR calibrated measurements from the device, and recalculate + // them as percentages relative to the strongest spectral component... + const buffer = Buffer.from([0.0, 0.0, 0.0, 0.0]); + GetVirtRegMultipleValues(I2C_BUS, I2C_ADDRESS_AS7262, 0x14, buffer); + data[0] = buffer.readFloatBE(0); + GetVirtRegMultipleValues(I2C_BUS, I2C_ADDRESS_AS7262, 0x18, buffer); + data[1] = buffer.readFloatBE(0); + GetVirtRegMultipleValues(I2C_BUS, I2C_ADDRESS_AS7262, 0x1c, buffer); + data[2] = buffer.readFloatBE(0); + GetVirtRegMultipleValues(I2C_BUS, I2C_ADDRESS_AS7262, 0x20, buffer); + data[3] = buffer.readFloatBE(0); + GetVirtRegMultipleValues(I2C_BUS, I2C_ADDRESS_AS7262, 0x24, buffer); + data[4] = buffer.readFloatBE(0); + GetVirtRegMultipleValues(I2C_BUS, I2C_ADDRESS_AS7262, 0x28, buffer); + data[5] = buffer.readFloatBE(0); + + var maximum = Math.max(data[0], data[1], data[2], data[3], data[4], data[5]); + data[0] = Math.round((data[0]/maximum)*100); + data[1] = Math.round((data[1]/maximum)*100); + data[2] = Math.round((data[2]/maximum)*100); + data[3] = Math.round((data[3]/maximum)*100); + data[4] = Math.round((data[4]/maximum)*100); + data[5] = Math.round((data[5]/maximum)*100); + + // ==================== + + return data; +} + +// ==================== + +function Log() +{ + if (outputLogs) + { + // ##### Below: Console output in regular 3-bit colour... :| ##### + // console.log("Breakout Gardener -> AS7262 -> Violet \x1b[97;45m %s%% \x1b[0m / Blue \x1b[97;44m %s%% \x1b[0m / Green \x1b[97;42m %s%% \x1b[0m / Yellow \x1b[97;43m %s%% \x1b[0m / Orange \x1b[97;43m %s%% \x1b[0m / Red \x1b[97;41m %s%% \x1b[0m.", data[0], data[1], data[2], data[3], data[4], data[5]); + + // ##### Below: Console output in much nicer looking 24-bit colour! :D ##### + console.log("Breakout Gardener -> AS7262 -> Violet \033[48;2;102;0;1702m %s%% \033[m / Blue \033[48;2;0;0;1702m %s%% \033[m / Green \033[48;2;0;136;02m %s%% \033[m / Yellow \033[48;2;170;136;02m %s%% \033[m / Orange \033[48;2;170;68;02m %s%% \033[m / Red \033[48;2;170;0;02m %s%% \033[m.", data[0], data[1], data[2], data[3], data[4], data[5]); + + // var deviceTemp = GetVirtualRegister(I2C_BUS, I2C_ADDRESS_AS7262, 0x06); + // console.log("Breakout Gardener -> AS7262 -> The device temperature is %s °C.", deviceTemp); + } +} + +// ==================== + +function Display(refreshAll) +{ + + Get(); + + if (SH1107.IsAvailable()) + { + if (refreshAll) + { + SH1107.Off(); + SH1107.Clear(); + + SH1107.DrawTextSmall("V", 2, 1, false); + SH1107.DrawTextSmall("B", 2, 3, false); + SH1107.DrawTextSmall("G", 2, 5, false); + SH1107.DrawTextSmall("Y", 2, 7, false); + SH1107.DrawTextSmall("O", 2, 9, false); + SH1107.DrawTextSmall("R", 2, 11, false); + + SH1107.DrawTextSmall("AS7262", 39, 16, false); + } + + SH1107.DrawMeterBar(data[0], 1); + SH1107.DrawMeterBar(data[1], 3); + SH1107.DrawMeterBar(data[2], 5); + SH1107.DrawMeterBar(data[3], 7); + SH1107.DrawMeterBar(data[4], 9); + SH1107.DrawMeterBar(data[5], 11); + + if (refreshAll) SH1107.On(); + } + + // ==================== + + if (IS31FL3731_RGB.IsAvailable()) + { + const icon = [0x000000, 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, + 0x000000, 0x000000, 0x000000, 0x000000, 0x000000 ]; + + // As we have 6 channels to display, but only 5 rows available, + // let's alternate between VBG and YOR on 3 rows instead... + if (!alternatingDisplay) + { + // Violet + if (data[0] > 10) icon[5] = 0x550099; + else icon[5] = 0x2a004c; + if (data[0] > 30) icon[6] = 0x550099; + else if (data[0] > 20) icon[6] = 0x2a004c; + if (data[0] > 50) icon[7] = 0x550099; + else if (data[0] > 40) icon[7] = 0x2a004c; + if (data[0] > 70) icon[8] = 0x550099; + else if (data[0] > 60) icon[8] = 0x2a004c; + if (data[0] > 90) icon[9] = 0x550099; + else if (data[0] > 80) icon[9] = 0x2a004c; + + // Blue + if (data[1] > 10) icon[10] = 0x0000aa; + else icon[10] = 0x000055; + if (data[1] > 30) icon[11] = 0x0000aa; + else if (data[1] > 20) icon[11] = 0x000055; + if (data[1] > 50) icon[12] = 0x0000aa; + else if (data[1] > 40) icon[12] = 0x000055; + if (data[1] > 70) icon[13] = 0x0000aa; + else if (data[1] > 60) icon[13] = 0x000055; + if (data[1] > 90) icon[14] = 0x0000aa; + else if (data[1] > 80) icon[14] = 0x000055; + + // Green + if (data[2] > 10) icon[15] = 0x008800; + else icon[15] = 0x004400; + if (data[2] > 30) icon[16] = 0x008800; + else if (data[2] > 20) icon[16] = 0x004400; + if (data[2] > 50) icon[17] = 0x008800; + else if (data[2] > 40) icon[17] = 0x004400; + if (data[2] > 70) icon[18] = 0x008800; + else if (data[2] > 60) icon[18] = 0x004400; + if (data[2] > 90) icon[19] = 0x008800; + else if (data[2] > 80) icon[19] = 0x004400; + + alternatingDisplay = true; + } + else + { + // Yellow + if (data[3] > 10) icon[5] = 0xaa8800; + else icon[5] = 0x554400; + if (data[3] > 30) icon[6] = 0xaa8800; + else if (data[3] > 20) icon[6] = 0x554400; + if (data[3] > 50) icon[7] = 0xaa8800; + else if (data[3] > 40) icon[7] = 0x554400; + if (data[3] > 70) icon[8] = 0xaa8800; + else if (data[3] > 60) icon[8] = 0x554400; + if (data[3] > 90) icon[9] = 0xaa8800; + else if (data[3] > 80) icon[9] = 0x554400; + + // Orange + if (data[4] > 10) icon[10] = 0xaa4400; + else icon[10] = 0x552200; + if (data[4] > 30) icon[11] = 0xaa4400; + else if (data[4] > 20) icon[11] = 0x552200; + if (data[4] > 50) icon[12] = 0xaa4400; + else if (data[4] > 40) icon[12] = 0x552200; + if (data[4] > 70) icon[13] = 0xaa4400; + else if (data[4] > 60) icon[13] = 0x552200; + if (data[4] > 90) icon[14] = 0xaa4400; + else if (data[4] > 80) icon[14] = 0x552200; + + // Red + if (data[5] > 10) icon[15] = 0xaa0000; + else icon[15] = 0x550000; + if (data[5] > 30) icon[16] = 0xaa0000; + else if (data[5] > 20) icon[16] = 0x550000; + if (data[5] > 50) icon[17] = 0xaa0000; + else if (data[5] > 40) icon[17] = 0x550000; + if (data[5] > 70) icon[18] = 0xaa0000; + else if (data[5] > 60) icon[18] = 0x550000; + if (data[5] > 90) icon[19] = 0xaa0000; + else if (data[5] > 80) icon[19] = 0x550000; + + alternatingDisplay = false; + } + + IS31FL3731_RGB.Display(icon); + } + + // ==================== + + if (IS31FL3731_WHITE.IsAvailable()) + { + IS31FL3731_WHITE.DrawMeter(data[0], 0); + IS31FL3731_WHITE.DrawMeter(data[1], 1); + IS31FL3731_WHITE.DrawMeter(data[2], 2); + IS31FL3731_WHITE.DrawMeter(data[3], 3); + IS31FL3731_WHITE.DrawMeter(data[4], 4); + IS31FL3731_WHITE.DrawMeter(data[5], 5); + IS31FL3731_WHITE.DrawMeter(255, 6); + } + + // ==================== + + if (HT16K33.IsAvailable()) + { + var colour = 'RED'; + if (data[0] == 100) colour = 'VIOLET'; + if (data[1] == 100) colour = 'BLUE'; + if (data[2] == 100) colour = 'GREEN'; + if (data[3] == 100) colour = 'YELLOW'; + if (data[4] == 100) colour = 'ORANGE'; + if (data[5] == 100) colour = 'RED'; + HT16K33.Display(colour); + } + + // ==================== + + if (refreshAll) Log(); +} + +// ================================================================================ diff --git a/modules/BMP280.js b/modules/BMP280.js index 20b16f7..ccd220a 100644 --- a/modules/BMP280.js +++ b/modules/BMP280.js @@ -7,6 +7,10 @@ const i2c = require('i2c-bus'); // -> https://github.com/fivdi/i2c-bus const SH1107 = require('./SH1107.js'); const IS31FL3731_RGB = require('./IS31FL3731_RGB.js'); const IS31FL3731_WHITE = require('./IS31FL3731_WHITE.js'); +const HT16K33 = require('./HT16K33.js'); + +const { performance } = require('perf_hooks'); +function Wait(msecs) { const start = performance.now(); while(performance.now() - start < msecs); } // Helper function module.exports = { Identify: Identify, @@ -64,7 +68,8 @@ function Start(logs, debug) if (debug) showDebug = true; // Configure the device... - I2C_BUS.writeByteSync(I2C_ADDRESS_BMP280, 0xe0, 0xb6); // Reset the sensor + I2C_BUS.writeByteSync(I2C_ADDRESS_BMP280, 0xe0, 0xb6); // Reset the sensor (might not be needed anymore since we started general I2C soft resetting on startup, but keeping it for now) + Wait(25); // "Guesstimate" (not specified in the datasheet) I2C_BUS.writeByteSync(I2C_ADDRESS_BMP280, 0xf4, 0b10110111); // Temp oversampling 16x, Pressure oversampling 16x, Normal (continuous) mode I2C_BUS.writeByteSync(I2C_ADDRESS_BMP280, 0xf5, 0b10010000); // 500 ms interval ("inactive duration"), IIR filter @ standard resolution @@ -189,7 +194,17 @@ function Display(refreshAll) if (IS31FL3731_WHITE.IsAvailable()) { IS31FL3731_WHITE.DrawString(Math.round(data[0]).toString()); - } + } + + // ==================== + + if (HT16K33.IsAvailable()) + { + // var temperature = data[0].toFixed(1) + 'C'; + // HT16K33.Display(temperature); + var pressure = data[1].toFixed(0); + HT16K33.Display(pressure); + } // ==================== diff --git a/modules/Clock.js b/modules/Clock.js index 7393862..c8dd19b 100644 --- a/modules/Clock.js +++ b/modules/Clock.js @@ -6,6 +6,7 @@ const SH1107 = require('./SH1107.js'); const IS31FL3731_RGB = require('./IS31FL3731_RGB.js'); const IS31FL3731_WHITE = require('./IS31FL3731_WHITE.js'); +const HT16K33 = require('./HT16K33.js'); const DRV2605 = require('./DRV2605.js'); module.exports = { @@ -165,6 +166,15 @@ function Display(refreshAll) IS31FL3731_WHITE.Display(icon); } + // ==================== + + if (HT16K33.IsAvailable()) + { + // Display clock in HH.MM format... + var clock = time.slice(0,2) + '.' + time.slice(3,5); + HT16K33.Display(clock); + } + // ==================== if (DRV2605.IsAvailable()) diff --git a/modules/DS18B20.js b/modules/DS18B20.js index c5e1b5a..579770f 100644 --- a/modules/DS18B20.js +++ b/modules/DS18B20.js @@ -7,6 +7,7 @@ const { exec } = require('child_process'); const SH1107 = require('./SH1107.js'); const IS31FL3731_RGB = require('./IS31FL3731_RGB.js'); const IS31FL3731_WHITE = require('./IS31FL3731_WHITE.js'); +const HT16K33 = require('./HT16K33.js'); module.exports = { Identify: Identify, @@ -217,6 +218,14 @@ function Display(refreshAll) IS31FL3731_WHITE.DrawString(Math.round(data[0]).toString()); // Indoor temperature } + // ==================== + + if (HT16K33.IsAvailable()) + { + var indoors = data[0].toFixed(1) + 'C'; + HT16K33.Display(indoors); + } + // ==================== if (refreshAll) Log(); diff --git a/modules/Dashboard.js b/modules/Dashboard.js index f1b9bb2..4cd3115 100644 --- a/modules/Dashboard.js +++ b/modules/Dashboard.js @@ -8,10 +8,12 @@ const http = require('http'); const ADS1015 = require('./ADS1015.js'); const ADT7410 = require('./ADT7410.js'); const ADXL343 = require('./ADXL343.js'); +const AS7262 = require('./AS7262.js'); const BMP280 = require('./BMP280.js'); const CAP1166 = require('./CAP1166.js'); const DRV2605 = require('./DRV2605.js'); const DS18B20 = require('./DS18B20.js'); +const HT16K33 = require('./HT16K33.js'); const IS31FL3731_RGB = require('./IS31FL3731_RGB.js'); const IS31FL3731_WHITE = require('./IS31FL3731_WHITE.js'); const KEBA_P30 = require('./KEBA_P30.js'); @@ -138,7 +140,7 @@ function Start(port, multiple, version, logs, debug) response.write("
Reload
"); response.write("\n
Visualize
"); // -> Open Grafana [host:3000] in a new window/tab response.write("\n
Shop
"); - response.write("\n
Pimoroni (UK)
Adafruit Industries (US)
"); + response.write("\n
Pimoroni (UK)
Adafruit Industries (US)
"); // ### TO-DO / POSSIBLE FUTURE ENHANCEMENT: OPEN GRAFANA IN AN EMBEDDED IFRAME INSTEAD OF IN A NEW TAB/WINDOW ? ### // response.write("\n
"); @@ -147,25 +149,25 @@ function Start(port, multiple, version, logs, debug) // ==================== - var columnSpanning = 10; - if (!dashboardMultipleRows) columnSpanning = 20; + var columnSpanning = 11; + if (!dashboardMultipleRows) columnSpanning = 21; var cpuLoad = SYSTEM.GetCPULoad(); var cpuTemperature = SYSTEM.GetCPUTemperature(); var memoryUsageInPercent = SYSTEM.GetMemoryUsageInPercent(); - if (cpuLoad.Cores.length == 4) response.write("SYSTEM  CPU load per core:   ① " + cpuLoad.Cores[0].toFixed(1) + "%   ② " + cpuLoad.Cores[1].toFixed(1) + "%   ③ " + cpuLoad.Cores[2].toFixed(1) + "%   ④ " + cpuLoad.Cores[3].toFixed(1) + "%   │   Across all cores:   " + cpuLoad.AllCores.toFixed(0) + "%   │   CPU temperature:   " + cpuTemperature.toFixed(1) + " °C   │   Memory usage:   " + memoryUsageInPercent.toFixed(0) + "%   │   Timestamp:   " + CLOCK.Get() + "\n\n"); - else response.write("SYSTEM  CPU load across all cores:   " + cpuLoad.AllCores.toFixed(0) + "%   │   CPU temperature:   " + cpuTemperature.toFixed(1) + " °C   │   Memory usage:   " + memoryUsageInPercent.toFixed(0) + "%   │   Timestamp:   " + CLOCK.Get() + "\n"); + if (cpuLoad.Cores.length == 4) response.write("\nSYSTEM  CPU load per core:   ① " + cpuLoad.Cores[0].toFixed(1) + "%   ② " + cpuLoad.Cores[1].toFixed(1) + "%   ③ " + cpuLoad.Cores[2].toFixed(1) + "%   ④ " + cpuLoad.Cores[3].toFixed(1) + "%   │   Across all cores:   " + cpuLoad.AllCores.toFixed(0) + "%   │   CPU temperature:   " + cpuTemperature.toFixed(1) + " °C   │   Memory usage:   " + memoryUsageInPercent.toFixed(0) + "%   │   Timestamp:   " + CLOCK.Get() + "\n"); + else response.write("\nSYSTEM  CPU load across all cores:   " + cpuLoad.AllCores.toFixed(0) + "%   │   CPU temperature:   " + cpuTemperature.toFixed(1) + " °C   │   Memory usage:   " + memoryUsageInPercent.toFixed(0) + "%   │   Timestamp:   " + CLOCK.Get() + "\n"); - response.write("\n"); + response.write("\n"); // ==================== if (ADS1015.IsAvailable()) { var ads1015 = ADS1015.Get(); - response.write(" ADS1015

S0:   " + ads1015[0].toFixed(2) + " V
S1:   " + ads1015[1].toFixed(2) + " V
S2:   " + ads1015[2].toFixed(2) + " V
S3:   " + ads1015[3].toFixed(2) + " V

D01:   " + ads1015[4].toFixed(2) + " V
D23:   " + ads1015[5].toFixed(2) + " V"); + response.write("\n ADS1015

S0:   " + ads1015[0].toFixed(2) + " V
S1:   " + ads1015[1].toFixed(2) + " V
S2:   " + ads1015[2].toFixed(2) + " V
S3:   " + ads1015[3].toFixed(2) + " V

D01:   " + ads1015[4].toFixed(2) + " V
D23:   " + ads1015[5].toFixed(2) + " V"); } - else response.write(" ADS1015"); + else response.write("\n ADS1015"); // ==================== @@ -174,7 +176,7 @@ function Start(port, multiple, version, logs, debug) var adt7410 = ADT7410.Get(); response.write("\n ADT7410

Temperature:
" + adt7410[0].toFixed(1) + " °C"); } - else response.write(" ADT7410"); + else response.write("\n ADT7410"); // ==================== @@ -190,7 +192,40 @@ function Start(port, multiple, version, logs, debug) } else response.write("
None"); } - else response.write(" ADXL343"); + else response.write("\n ADXL343"); + + // ==================== + + if (AS7262.IsAvailable()) + { + var as7262 = AS7262.Get(); + + response.write("\n AS7262

"); + + for (var n=0; n<=5; n++) + { + var meter = ''; + + if (n == 0) meter = ""; // Violet + if (n == 1) meter = ""; // Blue + if (n == 2) meter = ""; // Green + if (n == 3) meter = ""; // Yellow + if (n == 4) meter = ""; // Orange + if (n == 5) meter = ""; // Red + + if (as7262[n] > 0) meter += '█'; + if (as7262[n] > 20) meter += '█'; + if (as7262[n] > 40) meter += '█'; + if (as7262[n] > 60) meter += '█'; + if (as7262[n] > 80) meter += '█'; + + meter += '
'; + response.write(meter); + } + + response.write(""); + } + else response.write("\n AS7262"); // ==================== @@ -199,7 +234,7 @@ function Start(port, multiple, version, logs, debug) var bmp280 = BMP280.Get(); response.write("\n BMP280

Pressure:
" + bmp280[1].toFixed(0) + " hPa

Temperature:
" + bmp280[0].toFixed(1) + " °C"); } - else response.write(" BMP280"); + else response.write("\n BMP280"); // ==================== @@ -237,7 +272,7 @@ function Start(port, multiple, version, logs, debug) response.write("\n CAP1166

Touch:
" + touch + "

LEDs:
" + leds + ""); } - else response.write(" CAP1166"); + else response.write("\n CAP1166"); // ==================== @@ -245,7 +280,7 @@ function Start(port, multiple, version, logs, debug) { response.write("\n DRV2605

Device
enabled"); } - else response.write(" DRV2605"); + else response.write("\n DRV2605"); // ==================== @@ -255,7 +290,18 @@ function Start(port, multiple, version, logs, debug) if (ds18b20.length > 1) response.write("\n DS18B20

Indoors:
" + ds18b20[0] + " °C

Outdoors:
" + ds18b20[1] + " °C"); else response.write("\n DS18B20

Temperature:
" + ds18b20[0] + " °C"); } - else response.write(" DS18B20"); + else response.write("\n DS18B20"); + + // ==================== + + if (HT16K33.IsAvailable()) + { + var ht16k33 = HT16K33.Get(); +// if (ht16k33.length > 0) response.write("\n HT16K33

Device
enabled

" + ht16k33 + ""); +// else response.write("\n HT16K33

Device
enabled"); + response.write("\n HT16K33

\"" + ht16k33 + "\""); + } + else response.write("\n HT16K33"); // ==================== @@ -276,7 +322,7 @@ function Start(port, multiple, version, logs, debug) tempString += ""; response.write(tempString); } - else response.write(" IS31FL3731 (RGB)"); + else response.write("\n IS31FL3731 (RGB)"); // ==================== @@ -298,7 +344,12 @@ function Start(port, multiple, version, logs, debug) tempString += ""; response.write(tempString); } - else response.write(" IS31FL3731 (W)"); + else response.write("\n IS31FL3731 (W)"); + + // ==================== + + // ### MOVE TO THE NEXT ROW IF DASHBOARD MULTIPLE ROW DISPLAY MODE IS ENABLED ### + if (dashboardMultipleRows) response.write("\n\n"); // ==================== @@ -315,14 +366,9 @@ function Start(port, multiple, version, logs, debug) if (kebap30[0] == 9) state = '(Checking)'; response.write("\n KEBA P30

State:
" + state + "

Power:
" + kebap30[1] + " kW (" + kebap30[2] + "%)

Energy transferred:
" + kebap30[3] + " kWh"); - response.write("\n

Per phase:
" + kebap30[4] + " / " + kebap30[5] + " / " + kebap30[6] + " V
" + kebap30[7] + " / " + kebap30[8] + " / " + kebap30[9] + " A"); + response.write("

Per phase:
" + kebap30[4] + " / " + kebap30[5] + " / " + kebap30[6] + " V
" + kebap30[7] + " / " + kebap30[8] + " / " + kebap30[9] + " A"); } - else response.write(" KEBA P30"); - - // ==================== - - // ### MOVE TO THE NEXT ROW IF DASHBOARD MULTIPLE ROW DISPLAY MODE IS ENABLED ### - if (dashboardMultipleRows) response.write("\n\n"); + else response.write("\n KEBA P30"); // ==================== @@ -331,7 +377,7 @@ function Start(port, multiple, version, logs, debug) var lsm303d = LSM303D.Get(); response.write("\n LSM303D

Accelerometer:
X " + lsm303d[3].toFixed(2) + "
Y " + lsm303d[4].toFixed(2) + "
Z " + lsm303d[5].toFixed(2) + "
Roll " + lsm303d[12].toFixed(0) + "
Pitch " + lsm303d[13].toFixed(0) + "

Magnetometer:
X " + lsm303d[9].toFixed(2) + "
Y " + lsm303d[10].toFixed(2) + "
Z " + lsm303d[11].toFixed(2) + "
Heading " + lsm303d[14].toFixed(0) + "°"); } - else response.write(" LSM303D"); + else response.write("\n LSM303D"); // ==================== @@ -340,7 +386,7 @@ function Start(port, multiple, version, logs, debug) var mcp9808 = MCP9808.Get(); response.write("\n MCP9808

Temperature:
" + mcp9808[0].toFixed(1) + " °C"); } - else response.write(" MCP9808"); + else response.write("\n MCP9808"); // ==================== @@ -355,9 +401,9 @@ function Start(port, multiple, version, logs, debug) else if (sgp30[0] > 65) iaq = "███████  2
Good"; else iaq = "███████  1
Excellent"; - response.write("\n SGP30

TVOC:
" + sgp30[0].toFixed(0) + " PPB
CO2eq:
" + sgp30[1].toFixed(0) + " PPM

Ethanol (C2H6O):
" + sgp30[2].toFixed(0) + " PPM
Hydrogen (H2):
" + sgp30[3].toFixed(0) + " PPM

" + iaq + "\n"); + response.write("\n SGP30

TVOC:
" + sgp30[0].toFixed(0) + " PPB
CO2eq:
" + sgp30[1].toFixed(0) + " PPM

Ethanol (C2H6O):
" + sgp30[2].toFixed(0) + " PPM
Hydrogen (H2):
" + sgp30[3].toFixed(0) + " PPM

" + iaq + ""); } - else response.write(" SGP30"); + else response.write("\n SGP30"); // ==================== @@ -365,7 +411,7 @@ function Start(port, multiple, version, logs, debug) { response.write("\n SH1107

Device
enabled"); } - else response.write(" SH1107"); + else response.write("\n SH1107"); // ==================== @@ -374,11 +420,11 @@ function Start(port, multiple, version, logs, debug) var sht31d = SHT31D.Get(); response.write("\n SHT31D

Humidity:
" + sht31d[0].toFixed(0) + " %

Temperature:
" + sht31d[1].toFixed(1) + " °C"); } - else response.write(" SHT31D"); + else response.write("\n SHT31D"); // ==================== - response.write(" SOUNDS

(upcoming
module)"); + response.write("\n SOUNDS

(upcoming
module)"); // ==================== @@ -389,7 +435,7 @@ function Start(port, multiple, version, logs, debug) var color = (0x1000000 + rgb).toString(16).slice(1); response.write("\n TCS3472

Light:   " + tcs3472[5] + " lux
Temp:   " + tcs3472[4] + " °K

Clear:   " + tcs3472[0] + "
Red:   " + tcs3472[1] + "
Green:   " + tcs3472[2] + "
Blue:   " + tcs3472[3] + "

███████
#" + color + ""); } - else response.write(" TCS3472"); + else response.write("\n TCS3472"); // ==================== @@ -397,7 +443,7 @@ function Start(port, multiple, version, logs, debug) { response.write("\n TRACKBALL

Device
enabled"); } - else response.write(" TRACKBALL"); + else response.write("\n TRACKBALL"); // ==================== @@ -406,7 +452,7 @@ function Start(port, multiple, version, logs, debug) var vcnl4010 = VCNL4010.Get(); response.write("\n VCNL4010

Light:
" + vcnl4010[0] + " lux

Proximity:
" + vcnl4010[1] + ""); } - else response.write(" VCNL4010"); + else response.write("\n VCNL4010"); // ==================== @@ -423,12 +469,12 @@ function Start(port, multiple, version, logs, debug) response.write("\n VEML6075

UVA:
" + veml6075[0].toFixed(0) + "
UVB:
" + veml6075[1].toFixed(0) + "

UV Index:
" + veml6075[2].toFixed(1) + "

" + uvi + "\n"); } - else response.write(" VEML6075"); + else response.write("\n VEML6075"); // ==================== var tempString = ''; - tempString = '\n\n

Breakout Gardener ' + softwareVersion + ', running on Node.js® ' + process.version + ' and using the i2c-bus library. © 2018-2019 @xoblite. All trademarks are property of their respective owners.\n'; + tempString = '\n\n\n

Breakout Gardener ' + softwareVersion + ' (changes), running on Node.js® ' + process.version + ' and using the i2c-bus library. © 2018-2019 @xoblite. All trademarks are property of their respective owners.\n'; response.write(tempString); response.write("\n\n"); diff --git a/modules/HT16K33.js b/modules/HT16K33.js new file mode 100644 index 0000000..54b0580 --- /dev/null +++ b/modules/HT16K33.js @@ -0,0 +1,274 @@ +// ============================================================================================ +// --- BREAKOUT GARDENER :: MODULES :: HT16K33 --- +// (c) 2018-2019 Karl-Henrik Henriksson - breakouts*xoblite.net - http://breakouts.xoblite.net/ +// ============================================================================================ + +const i2c = require('i2c-bus'); // -> https://github.com/fivdi/i2c-bus + +module.exports = { + Identify: Identify, + IsAvailable: IsAvailable, + Start: Start, + Stop: Stop, + On: On, + Off: Off, + Clear: Clear, + // ----------- + Display: Display, + Get: Get, +}; + +// ================================================================================ + +// ---------------------------------------------------------------------------------------- +// === Holtek HT16K33 LED controller driver chip === +// * Product Page -> https://www.holtek.com/productdetail/-/vg/HT16K33 +// * Datasheet -> https://www.holtek.com/documents/10179/116711/HT16K33v120.pdf +// ---------------------------------------------------------------------------------------- + +var I2C_BUS = 0, I2C_ADDRESS_HT16K33 = 0; +var currentlyDisplayed = ''; +var pulseInterval = null, pulseLevel = 0xe0; +var outputLogs = false, showDebug = false; + +// ==================== + +function Identify(bus, address) +{ + if (I2C_ADDRESS_HT16K33 > 0) return false; + + // Note: There doesn't seem to be a way to identify the + // HT16K33 device unfortunately [?], so here we go... + I2C_BUS = bus; + I2C_ADDRESS_HT16K33 = address; + return true; +} + +// ==================== + +function IsAvailable() +{ + if (I2C_ADDRESS_HT16K33) return true; + else return false; +} + +// ==================== + +function Start(logs, debug) +{ + if (logs) outputLogs = true; + if (debug) showDebug = true; + + Off(); + + // Configure the device... + I2C_BUS.sendByteSync(I2C_ADDRESS_HT16K33, 0xa0); // Set to ROW driver output (default) + I2C_BUS.sendByteSync(I2C_ADDRESS_HT16K33, 0x00); // Set display RAM data address pointer (default) + + Clear(); + On(); +} + +function Stop() +{ + clearInterval(pulseInterval); + Off(); +} + +// ==================== + +function On() +{ + // Turn on the display... + I2C_BUS.sendByteSync(I2C_ADDRESS_HT16K33, 0x21); // System setup -> Turn on system oscillator (normal operation mode) + I2C_BUS.sendByteSync(I2C_ADDRESS_HT16K33, 0x81); // Display setup -> Turn on display + blinking off + // if (outputLogs) console.log("Breakout Gardener -> HT16K33 -> Turning on the display."); +} +function Off() +{ + // Turn off the display... + I2C_BUS.sendByteSync(I2C_ADDRESS_HT16K33, 0x80); // Display setup -> Turn off display + I2C_BUS.sendByteSync(I2C_ADDRESS_HT16K33, 0x20); // System setup -> Turn off system oscillator (standby mode) + // if (outputLogs) console.log("Breakout Gardener -> HT16K33 -> Turning off the display."); +} +function Clear() +{ + // Clear all pixels... + var buffer = Buffer.from([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); + I2C_BUS.writeI2cBlockSync(I2C_ADDRESS_HT16K33, 0x00, buffer.length, buffer); + currentlyDisplayed = ''; + // if (showDebug) console.log("Breakout Gardener -> HT16K33 -> Clearing the display..."); +} + +// ================================================================================ + +function Pulse() // Display() helper function +{ + pulseLevel++; + if (pulseLevel < 0xef) I2C_BUS.sendByteSync(I2C_ADDRESS_HT16K33, pulseLevel); // Increase display brightness... (in 16 steps, cf. values 0xe0-0xef -> 1/16 to 16/16 duty pulse width) + else clearInterval(pulseInterval); +} + +const char0 = [0x3f, 0x0c]; +const char1 = [0x06, 0x04]; +const char2 = [0xdb, 0x00]; +const char3 = [0xcf, 0x00]; +const char4 = [0xe6, 0x00]; +const char5 = [0xed, 0x00]; +const char6 = [0xfd, 0x00]; +const char7 = [0x07, 0x00]; +const char8 = [0xff, 0x00]; +const char9 = [0xef, 0x00]; + +const charA = [0xf7, 0x00]; +const charB = [0x8f, 0x12]; +const charC = [0x39, 0x00]; +const charD = [0x0f, 0x12]; +const charE = [0xf9, 0x00]; +const charF = [0xf1, 0x00]; +const charG = [0xbd, 0x00]; +const charH = [0xf6, 0x00]; +const charI = [0x09, 0x12]; +const charJ = [0x1e, 0x00]; +const charK = [0x70, 0x24]; +const charL = [0x38, 0x00]; +const charM = [0x36, 0x05]; +const charN = [0x36, 0x21]; +const charO = [0x3f, 0x00]; +const charP = [0xf3, 0x00]; +const charQ = [0x3f, 0x20]; +const charR = [0xf3, 0x20]; +const charS = [0x8d, 0x01]; +const charT = [0x01, 0x12]; +const charU = [0x3e, 0x00]; +const charV = [0x30, 0x0c]; +const charW = [0x36, 0x28]; +const charX = [0x00, 0x2d]; +const charY = [0x00, 0x15]; +const charZ = [0x09, 0x0c]; + +const charPlus = [0xc0, 0x12]; +const charMinus = [0xc0, 0x00]; +const charMoreThan = [0x00, 0x09]; +const charLessThan = [0x00, 0x24]; +const charPercent = [0x24, 0x0c]; +const charSpace = [0x00, 0x00]; +const charAsterisk = [0xc0, 0x3f]; +const charHash = [0xff, 0x3f]; +const charPipe = [0x00, 0x12]; +const charSlash = [0x00, 0x0c]; +const charBackslash = [0x00, 0x21]; + +// ==================== + +function Display(input) +{ + clearInterval(pulseInterval); + I2C_BUS.sendByteSync(I2C_ADDRESS_HT16K33, 0xe0); // Set the display brightness to the lowest setting... (preparing for a new message to be displayed) + + var displayBuffer = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + + if (input.length == 0) + { + // if (showDebug) console.log("Breakout Gardener -> HT16K33 -> No input provided, displaying default message... (\"*BG*\")"); + currentlyDisplayed = '*BG*'; + displayBuffer = [0xc0, 0x3f, 0x8f, 0x12, 0xbd, 0x00, 0xc0, 0x3f]; // Display *BG* + } + else + { + // if (showDebug) console.log("Breakout Gardener -> HT16K33 -> Displaying \"%s\"...", input); + currentlyDisplayed = ''; + + var ptr = 0; + for (var n=0; n= 8) break; // Allow maximum 4 characters (for now at least, scrolling may come later...) + + currentlyDisplayed += input[n]; // Keep track of what's being displayed... (might later also be shown in the Dashboard) + + if (input[n] == '.') continue; // Note: Decimal points are handled along with the preceding character (see below) + + var charToDraw = charSpace; + switch (input[n]) + { + case '0': { charToDraw = char0; break; } + case '1': { charToDraw = char1; break; } + case '2': { charToDraw = char2; break; } + case '3': { charToDraw = char3; break; } + case '4': { charToDraw = char4; break; } + case '5': { charToDraw = char5; break; } + case '6': { charToDraw = char6; break; } + case '7': { charToDraw = char7; break; } + case '8': { charToDraw = char8; break; } + case '9': { charToDraw = char9; break; } + + case 'A': { charToDraw = charA; break; } + case 'B': { charToDraw = charB; break; } + case 'C': { charToDraw = charC; break; } + case 'D': { charToDraw = charD; break; } + case 'E': { charToDraw = charE; break; } + case 'F': { charToDraw = charF; break; } + case 'G': { charToDraw = charG; break; } + case 'H': { charToDraw = charH; break; } + case 'I': { charToDraw = charI; break; } + case 'J': { charToDraw = charJ; break; } + case 'K': { charToDraw = charK; break; } + case 'L': { charToDraw = charL; break; } + case 'M': { charToDraw = charM; break; } + case 'N': { charToDraw = charN; break; } + case 'O': { charToDraw = charO; break; } + case 'P': { charToDraw = charP; break; } + case 'Q': { charToDraw = charQ; break; } + case 'R': { charToDraw = charR; break; } + case 'S': { charToDraw = charS; break; } + case 'T': { charToDraw = charT; break; } + case 'U': { charToDraw = charU; break; } + case 'V': { charToDraw = charV; break; } + case 'W': { charToDraw = charW; break; } + case 'X': { charToDraw = charX; break; } + case 'Y': { charToDraw = charY; break; } + case 'Z': { charToDraw = charZ; break; } + + case '+': { charToDraw = charPlus; break; } + case '-': { charToDraw = charMinus; break; } + case '>': { charToDraw = charMoreThan; break; } + case '<': { charToDraw = charLessThan; break; } + case '%': { charToDraw = charPercent; break; } + case '*': { charToDraw = charAsterisk; break; } + case '#': { charToDraw = charHash; break; } + case '|': { charToDraw = charPipe; break; } + case '/': { charToDraw = charSlash; break; } + case '\\': { charToDraw = charBackslash; break; } + + default: { charToDraw = charSpace; break; } + } + + displayBuffer[ptr] = charToDraw[0]; + displayBuffer[ptr+1] = charToDraw[1]; + + if (input[n+1] == '.') displayBuffer[ptr+1] |= 0xc0; // Add decimal point? + + ptr += 2; + } + } + + // Update the display... + var buffer = Buffer.from(displayBuffer); + I2C_BUS.writeI2cBlockSync(I2C_ADDRESS_HT16K33, 0x00, buffer.length, buffer); + + // ...and gradually increase ("pulse") the display brightness up to the maximum! + pulseLevel = 0xe0; + pulseInterval = setInterval(Pulse, 100); + + // if (showDebug && !fillDisplay) console.log("Breakout Gardener -> HT16K33 -> Displaying message..."); +} + +// ==================== + +function Get() { return currentlyDisplayed; } + +// ==================== + +function Log() { return; } + +// ================================================================================ diff --git a/modules/I2C.js b/modules/I2C.js index f341af1..1bd3872 100644 --- a/modules/I2C.js +++ b/modules/I2C.js @@ -8,9 +8,11 @@ const i2c = require('i2c-bus'); // -> https://github.com/fivdi/i2c-bus const ADS1015 = require('./ADS1015.js'); const ADT7410 = require('./ADT7410.js'); const ADXL343 = require('./ADXL343.js'); +const AS7262 = require('./AS7262.js'); const BMP280 = require('./BMP280.js'); const CAP1166 = require('./CAP1166.js'); const DRV2605 = require('./DRV2605.js'); +const HT16K33 = require('./HT16K33.js'); const IS31FL3731_RGB = require('./IS31FL3731_RGB.js'); const IS31FL3731_WHITE = require('./IS31FL3731_WHITE.js'); const LSM303D = require('./LSM303D.js'); @@ -34,16 +36,18 @@ module.exports = { // ================================================================================ // ----------------------------------------------------------------------------------- -// I2C addresses (as of Apr 2019) of some Pimoroni Breakouts and (p)HATs for reference +// I2C addresses (as of Dec 2019) of some Pimoroni Breakouts and (p)HATs for reference // ----------------------------------------------------------------------------------- // Breakouts: // * ADS1015 +/-24V ADC Breakout -> 0x48 (default) or 0x49 // * AS7262 6-channel Spectral Sensor (Spectrometer) Breakout -> 0x49 // * BH1745 Luminance and Colour Sensor Breakout -> 0x38 (default) or 0x39 +// * BME280 Breakout - Temperature, Pressure, Humidity Sensor -> 0x76 (default) or 0x77 // * BME680 Breakout - Air Quality, Temperature, Pressure, Humidity Sensor -> 0x76 (default) or 0x77 // * BMP280 Breakout - Temperature, Pressure, Altitude Sensor -> 0x76 (default) or 0x77 // * DRV2605L Linear Actuator Haptic Breakout -> 0x5A +// * DRV8830 DC Motor Driver Breakout -> 0x60 (default), 0x61, 0x63 or 0x64 // * ICM20948 9DoF Motion Sensor Breakout -> 0x68 (default) or 0x69 // * IS31FL3731 5x5 RGB LED Matrix Breakout -> 0x74 (default) or 0x77 // * IS31FL3731 11x7 White LED Matrix Breakout -> 0x75 (default) or 0x77 @@ -52,8 +56,13 @@ module.exports = { // * MAX30105 High-Sensitivity Optical ("Smoke Detection") Sensor Breakout -> 0x57 // * MCP9600 Thermocouple Amplifier Breakout -> 0x38 (default?) or 0x39 // * MLX90640 Thermal Camera Breakout -> 0x33 +// * MSA301 3DoF Motion Sensor Breakout -> 0x26 // * N76E003AQ20 MCU Based Trackball Breakout -> 0x0A (default) or 0x0B +// * RV3028 Real-Time Clock (RTC) Breakout -> 0x52 +// * SGP30 Indoor Air Quality (TVOC and CO2eq) Gas Sensor Breakout -> 0x58 // * SH1107 1.12" Mono OLED (128x128, white/black) Breakout -> 0x3c (default) or 0x3d +// * TCA9554A based HT0740 40V / 10A Switch Breakout -> 0x38 (default), 0x39, 0x3A or 0x3B +// * VEML6075 UVA/B Sensor Breakout -> 0x10 // * VL53L1X Time of Flight (ToF) Sensor Breakout -> 0x29 // Enviro pHAT: @@ -64,6 +73,10 @@ module.exports = { // * TCS3472 light and RGB colour sensor -> 0x29, // plus two LEDs for illumination controlled by GPIO #4 (pin 7) +// Four Letter pHAT: +// * HT16K33 LED controller driver chip -> 0x70 +// (controlling four 14-segment displays with green LEDs) + // Touch pHAT: // * CAP1166 capacitive touch and LED driver chip -> 0x2c // (controlling 6 capacitive touch buttons + 6 bright white under-mounted LEDs) @@ -86,7 +99,7 @@ var I2C_BUS = 0; // ==================== -function Start(outputLogs, showDebug) +function Start(reset, outputLogs, showDebug) { // Open the I2C bus... I2C_BUS = i2c.openSync(1, true); @@ -107,6 +120,15 @@ function Start(outputLogs, showDebug) // ==================== + if (reset) // Soft reset applicable I2C devices upon startup? (in my experience, e.g. the SGP30 becomes more reliable across certain types of restarts with this enabled) + { + console.log("Breakout Gardener -> INFO -> Starting: Soft resetting applicable I2C devices..."); + I2C_BUS.sendByteSync(0x00, 0x06); // Soft reset all [supporting/applicable] devices using the I2C General Call address (0x00)... + Wait(3000); // Wait 3 seconds, then continue... + } + + // ==================== + // Scan for devices on the opened I2C bus... var devices = I2C_BUS.scanSync(); if (devices.length > 0) @@ -124,8 +146,8 @@ function Start(outputLogs, showDebug) if (TRACKBALL.Identify(I2C_BUS, devices[i])) { console.log(" • \x1b[32mTRACKBALL\x1b[0m found at I2C address 0x%s (device %s on bus 1).", devices[i].toString(16), i); + break; } - break; } // ==================== @@ -136,8 +158,8 @@ function Start(outputLogs, showDebug) if (VEML6075.Identify(I2C_BUS, devices[i])) { console.log(" • \x1b[32mVEML6075\x1b[0m UVA/UVB sensor found at I2C address 0x%s (device %s on bus 1).", devices[i].toString(16), i); + break; } - break; } // ==================== @@ -148,8 +170,8 @@ function Start(outputLogs, showDebug) if (VCNL4010.Identify(I2C_BUS, devices[i])) { console.log(" • \x1b[32mVCNL4010\x1b[0m proximity/light sensor found at I2C address 0x%s (device %s on bus 1).", devices[i].toString(16), i); + break; } - break; } // ==================== @@ -161,8 +183,8 @@ function Start(outputLogs, showDebug) if (MCP9808.Identify(I2C_BUS, devices[i])) { console.log(" • \x1b[32mMCP9808\x1b[0m high accuracy temperature sensor found at I2C address 0x%s (device %s on bus 1).", devices[i].toString(16), i); + break; } - break; } // ==================== @@ -175,8 +197,8 @@ function Start(outputLogs, showDebug) if (LSM303D.Identify(I2C_BUS, devices[i])) { console.log(" • \x1b[32mLSM303D\x1b[0m accelerometer/magnetometer sensor found at I2C address 0x%s (device %s on bus 1).", devices[i].toString(16), i); + break; } - break; } // ==================== @@ -187,8 +209,8 @@ function Start(outputLogs, showDebug) if (TCS3472.Identify(I2C_BUS, devices[i])) { console.log(" • \x1b[32mTCS3472\x1b[0m light and RGB colour sensor found at I2C address 0x%s (device %s on bus 1).", devices[i].toString(16), i); + break; } - break; } // ==================== @@ -199,8 +221,8 @@ function Start(outputLogs, showDebug) if (CAP1166.Identify(I2C_BUS, devices[i])) { console.log(" • \x1b[32mCAP1166\x1b[0m capacitive touch and LED driver chip [Touch pHAT] found at I2C address 0x%s (device %s on bus 1).", devices[i].toString(16), i); + break; } - break; } // ==================== @@ -213,8 +235,8 @@ function Start(outputLogs, showDebug) if (SH1107.Identify(I2C_BUS, devices[i])) { console.log(" • \x1b[32mSH1107\x1b[0m 1.12\" Mono OLED (128x128, white/black) found at I2C address 0x%s (device %s on bus 1).", devices[i].toString(16), i); + break; } - break; } // ==================== @@ -225,8 +247,8 @@ function Start(outputLogs, showDebug) if (SHT31D.Identify(I2C_BUS, devices[i])) { console.log(" • \x1b[32mSHT31D\x1b[0m humidity and temperature sensor found at I2C address 0x%s (device %s on bus 1).", devices[i].toString(16), i); + break; } - break; } // ==================== @@ -237,26 +259,34 @@ function Start(outputLogs, showDebug) if (ADT7410.Identify(I2C_BUS, devices[i])) { console.log(" • \x1b[32mADT7410\x1b[0m high accuracy temperature sensor found at I2C address 0x%s (device %s on bus 1).", devices[i].toString(16), i); + break; } - break; } // ==================== case 0x49: // ##### Analog Devices ADT7410 ##### // -> Adafruit ADT7410 Sensor Breakout (secondary address @ pins "01" -> A1 == 0, A0 == 1) - // ##### Texas Instruments ADS1015 ##### + // ##### AMS AS7262 ##### + // -> Pimoroni AS7262 Sensor Breakout + // ##### Texas Instruments ADS1015 ##### // -> Pimoroni Enviro pHAT { if (ADT7410.Identify(I2C_BUS, devices[i])) { console.log(" • \x1b[32mADT7410\x1b[0m high accuracy temperature sensor found at I2C address 0x%s (device %s on bus 1).", devices[i].toString(16), i); + break; } - else if (ADS1015.Identify(I2C_BUS, devices[i])) // Nb. Needs to be last since there's no way to identify this device + else if (AS7262.Identify(I2C_BUS, devices[i])) + { + console.log(" • \x1b[32mAS7262\x1b[0m 6-channel spectral sensor (spectrometer) found at I2C address 0x%s (device %s on bus 1).", devices[i].toString(16), i); + break; + } + else if (ADS1015.Identify(I2C_BUS, devices[i])) // Nb. Needs to be last since there's no way to identify this device { console.log(" • \x1b[32mADS1015\x1b[0m 4-channel 5V tolerant 12-bit analog to digital sensor found at I2C address 0x%s (device %s on bus 1).", devices[i].toString(16), i); - } - break; + break; + } } // ==================== @@ -267,20 +297,21 @@ function Start(outputLogs, showDebug) if (ADXL343.Identify(I2C_BUS, devices[i])) { console.log(" • \x1b[32mADXL343\x1b[0m triple-axis accelerometer found at I2C address 0x%s (device %s on bus 1).", devices[i].toString(16), i); + break; } - break; } // ==================== - case 0x58: // ##### Sensirion SGP30 ##### + case 0x58: // ##### Sensirion SGP30 ##### + // -> Pimoroni SGP30 Sensor Breakout // -> Adafruit SGP30 Sensor Breakout { if (SGP30.Identify(I2C_BUS, devices[i])) { console.log(" • \x1b[32mSGP30\x1b[0m indoor air quality (TVOC and CO2eq -> IAQ) gas sensor found at I2C address 0x%s (device %s on bus 1).", devices[i].toString(16), i); + break; } - break; } // ==================== @@ -291,8 +322,20 @@ function Start(outputLogs, showDebug) if (DRV2605.Identify(I2C_BUS, devices[i])) { console.log(" • \x1b[32mDRV2605\x1b[0m haptic driver found at I2C address 0x%s (device %s on bus 1).", devices[i].toString(16), i); + break; + } + } + + // ==================== + + case 0x70: // ##### Holtek HT16K33 ##### + // -> Pimoroni Four Letter pHAT + { + if (HT16K33.Identify(I2C_BUS, devices[i])) + { + console.log(" • \x1b[32mHT16K33\x1b[0m LED controller driver chip [Four Letter pHAT] found at I2C address 0x%s (device %s on bus 1).", devices[i].toString(16), i); + break; } - break; } // ==================== @@ -303,8 +346,8 @@ function Start(outputLogs, showDebug) if (IS31FL3731_RGB.Identify(I2C_BUS, devices[i])) { console.log(" • \x1b[32mIS31FL3731\x1b[0m based RGB LED matrix found at I2C address 0x%s (device %s on bus 1).", devices[i].toString(16), i); + break; } - break; } // ==================== @@ -315,8 +358,8 @@ function Start(outputLogs, showDebug) if (IS31FL3731_WHITE.Identify(I2C_BUS, devices[i])) { console.log(" • \x1b[32mIS31FL3731\x1b[0m based White LED matrix found at I2C address 0x%s (device %s on bus 1).", devices[i].toString(16), i); + break; } - break; } // ==================== @@ -327,8 +370,8 @@ function Start(outputLogs, showDebug) if (BMP280.Identify(I2C_BUS, devices[i])) { console.log(" • \x1b[32mBMP280\x1b[0m temperature/pressure sensor found at I2C address 0x%s (device %s on bus 1).", devices[i].toString(16), i); + break; } - break; } // ==================== @@ -342,18 +385,20 @@ function Start(outputLogs, showDebug) if (BMP280.Identify(I2C_BUS, devices[i])) { console.log(" • \x1b[32mBMP280\x1b[0m temperature/pressure sensor found at I2C address 0x%s (device %s on bus 1).", devices[i].toString(16), i); + break; } else if (IS31FL3731_RGB.Identify(I2C_BUS, devices[i])) { console.log(" • \x1b[32mIS31FL3731\x1b[0m based RGB LED matrix found at I2C address 0x%s (device %s on bus 1).", devices[i].toString(16), i); + break; } /* else if (IS31FL3731_WHITE.Identify(I2C_BUS, devices[i])) { console.log(" • \x1b[32mIS31FL3731\x1b[0m based White LED matrix found at I2C address 0x%s (device %s on bus 1).", devices[i].toString(16), i); + break; } */ - break; } // ==================== diff --git a/modules/KEBA_P30.js b/modules/KEBA_P30.js index 8dfc757..8501084 100644 --- a/modules/KEBA_P30.js +++ b/modules/KEBA_P30.js @@ -7,6 +7,7 @@ const SH1107 = require('./SH1107.js'); const IS31FL3731_RGB = require('./IS31FL3731_RGB.js'); const IS31FL3731_WHITE = require('./IS31FL3731_WHITE.js'); const DRV2605 = require('./DRV2605.js'); +const HT16K33 = require('./HT16K33.js'); module.exports = { IsAvailable: IsAvailable, @@ -269,7 +270,22 @@ function Display(refreshAll) else { for (var n=66; n<77; n++) img[n] = 30; } IS31FL3731_WHITE.Display(img); - } + } + + // ==================== + + if (HT16K33.IsAvailable()) + { + var state = 'CHECKING'; + if (data[0] == 0) state = 'STARTING'; // Starting + if (data[0] == 1) state = 'UNPLUGGED'; // Unplugged (not ready) + if (data[0] == 2) state = 'PLUGGED'; // Plugged (not charging) + if (data[0] == 3) state = 'CHARGING'; // Charging + if (data[0] == 4) state = 'ERROR'; // Error + if (data[0] == 5) state = 'INTERRUPTED'; // Interrupted + + HT16K33.Display(state); + } // ==================== diff --git a/modules/Kompis.js b/modules/Kompis.js index 47898f8..c23a37c 100644 --- a/modules/Kompis.js +++ b/modules/Kompis.js @@ -7,6 +7,7 @@ const i2c = require('i2c-bus'); // -> https://github.com/fivdi/i2c-bus const SH1107 = require('./SH1107.js'); const IS31FL3731_RGB = require('./IS31FL3731_RGB.js'); const IS31FL3731_WHITE = require('./IS31FL3731_WHITE.js'); +const HT16K33 = require('./HT16K33.js'); module.exports = { Start: Start, @@ -250,6 +251,10 @@ function Display(refreshAll) // Show our little animated friend "Kompis"! :) else IS31FL3731_WHITE.Display(wNeutral); } + // ==================== + + if (HT16K33.IsAvailable()) HT16K33.Display(""); // -> Display *BG* (this is the default if no input is provided) + // ==================== animationFrame++; diff --git a/modules/LSM303D.js b/modules/LSM303D.js index 114a99b..4c15bc5 100644 --- a/modules/LSM303D.js +++ b/modules/LSM303D.js @@ -7,6 +7,7 @@ const i2c = require('i2c-bus'); // -> https://github.com/fivdi/i2c-bus const SH1107 = require('./SH1107.js'); const IS31FL3731_RGB = require('./IS31FL3731_RGB.js'); const IS31FL3731_WHITE = require('./IS31FL3731_WHITE.js'); +const HT16K33 = require('./HT16K33.js'); module.exports = { Identify: Identify, @@ -464,7 +465,23 @@ function Display(refreshAll) else if (hdng <= 293) IS31FL3731_WHITE.DrawString("W"); else if (hdng <= 338) IS31FL3731_WHITE.DrawString("NW"); else IS31FL3731_WHITE.DrawString("N"); - } + } + + // ==================== + + if (HT16K33.IsAvailable()) + { + // Display the rough compass heading... + if (hdng <= 23) HT16K33.Display("-N-"); + else if (hdng <= 68) HT16K33.Display("-NE-"); + else if (hdng <= 113) HT16K33.Display("-E-"); + else if (hdng <= 158) HT16K33.Display("-SE-"); + else if (hdng <= 203) HT16K33.Display("-S-"); + else if (hdng <= 248) HT16K33.Display("-SW-"); + else if (hdng <= 293) HT16K33.Display("-W-"); + else if (hdng <= 338) HT16K33.Display("-NW-"); + else HT16K33.Display("-N-"); + } // ==================== diff --git a/modules/MCP9808.js b/modules/MCP9808.js index 61273f4..d20d87d 100644 --- a/modules/MCP9808.js +++ b/modules/MCP9808.js @@ -7,6 +7,7 @@ const i2c = require('i2c-bus'); // -> https://github.com/fivdi/i2c-bus const SH1107 = require('./SH1107.js'); const IS31FL3731_RGB = require('./IS31FL3731_RGB.js'); const IS31FL3731_WHITE = require('./IS31FL3731_WHITE.js'); +const HT16K33 = require('./HT16K33.js'); module.exports = { Identify: Identify, @@ -157,7 +158,15 @@ function Display(refreshAll) if (IS31FL3731_WHITE.IsAvailable()) { IS31FL3731_WHITE.DrawString(Math.round(data[0]).toString()); - } + } + + // ==================== + + if (HT16K33.IsAvailable()) + { + var temperature = data[0].toFixed(1) + 'C'; + HT16K33.Display(temperature); + } // ==================== diff --git a/modules/Prometheus.js b/modules/Prometheus.js index 3e99aa3..4fa2401 100644 --- a/modules/Prometheus.js +++ b/modules/Prometheus.js @@ -8,6 +8,7 @@ const http = require('http'); const ADS1015 = require('./ADS1015.js'); const ADT7410 = require('./ADT7410.js'); const ADXL343 = require('./ADXL343.js'); +const AS7262 = require('./AS7262.js'); const BMP280 = require('./BMP280.js'); const DS18B20 = require('./DS18B20.js'); const KEBA_P30 = require('./KEBA_P30.js'); @@ -90,6 +91,20 @@ function Start(port, logs, debug) kv += 'adxl343_pitch ' + adxl343[7].toFixed(0) + '\n'; kv += 'adxl343_taps ' + adxl343[8] + '\n'; response.write(kv); + } + + // ==================== + + if (AS7262.IsAvailable()) + { + var as7262 = AS7262.Get(); + kv = 'as7262_violet ' + as7262[0] + '\n'; + kv += 'as7262_blue ' + as7262[1] + '\n'; + kv += 'as7262_green ' + as7262[2] + '\n'; + kv += 'as7262_yellow ' + as7262[3] + '\n'; + kv += 'as7262_orange ' + as7262[4] + '\n'; + kv += 'as7262_red ' + as7262[5] + '\n'; + response.write(kv); } // ==================== diff --git a/modules/SGP30.js b/modules/SGP30.js index a492ee1..faef4f9 100644 --- a/modules/SGP30.js +++ b/modules/SGP30.js @@ -7,6 +7,7 @@ const i2c = require('i2c-bus'); // -> https://github.com/fivdi/i2c-bus const SH1107 = require('./SH1107.js'); const IS31FL3731_RGB = require('./IS31FL3731_RGB.js'); const IS31FL3731_WHITE = require('./IS31FL3731_WHITE.js'); +const HT16K33 = require('./HT16K33.js'); const DRV2605 = require('./DRV2605.js'); const { performance } = require('perf_hooks'); @@ -317,7 +318,15 @@ function Display(refreshAll) // if (data[4] == 1) IS31FL3731_WHITE.DrawChar(data[4].toString(), 4); // else IS31FL3731_WHITE.DrawChar(data[4].toString(), 3); IS31FL3731_WHITE.DrawString(data[4].toString()); - } + } + + // ==================== + + if (HT16K33.IsAvailable()) + { + var iaq = 'IAQ' + data[4].toString(); + HT16K33.Display(iaq); + } // ==================== diff --git a/modules/SH1107.js b/modules/SH1107.js index 9777f6d..f25aa30 100644 --- a/modules/SH1107.js +++ b/modules/SH1107.js @@ -412,8 +412,9 @@ function DrawMeterBar(percentage, yoffset) { var meterLevel = percentage + 2; // Meter level 0-100% plus 2 pixels "always visible" -> Max 102 pixels wide - DisplaySetPosition(0, yoffset); // Top half... - for (var n=0; n<13; n++) I2C_BUS.writeByteSync(I2C_ADDRESS_SH1107, 0x40, 0x00); // Left padding -> (128-102)/2 = 13 pixels +// DisplaySetPosition(0, yoffset); // Top half... +// for (var n=0; n<13; n++) I2C_BUS.writeByteSync(I2C_ADDRESS_SH1107, 0x40, 0x00); // Left padding -> (128-102)/2 = 13 pixels + DisplaySetPosition(13, yoffset); // Top half... for (var n=0; n<102; n++) // 2+100 = 102 pixels meter bar { if (n < meterLevel) @@ -423,10 +424,11 @@ function DrawMeterBar(percentage, yoffset) } else I2C_BUS.writeByteSync(I2C_ADDRESS_SH1107, 0x40, 0x00); } - for (var n=0; n<13; n++) I2C_BUS.writeByteSync(I2C_ADDRESS_SH1107, 0x40, 0x00); // Right padding -> (128-102)/2 = 13 pixels +// for (var n=0; n<13; n++) I2C_BUS.writeByteSync(I2C_ADDRESS_SH1107, 0x40, 0x00); // Right padding -> (128-102)/2 = 13 pixels - DisplaySetPosition(0, yoffset+1); // Bottom half... - for (var n=0; n<13; n++) I2C_BUS.writeByteSync(I2C_ADDRESS_SH1107, 0x40, 0x00); +// DisplaySetPosition(0, yoffset+1); // Bottom half... +// for (var n=0; n<13; n++) I2C_BUS.writeByteSync(I2C_ADDRESS_SH1107, 0x40, 0x00); + DisplaySetPosition(13, yoffset+1); // Bottom half... for (var n=0; n<102; n++) { if (n < meterLevel) @@ -440,7 +442,7 @@ function DrawMeterBar(percentage, yoffset) else I2C_BUS.writeByteSync(I2C_ADDRESS_SH1107, 0x40, 0x00); } } - for (var n=0; n<13; n++) I2C_BUS.writeByteSync(I2C_ADDRESS_SH1107, 0x40, 0x00); +// for (var n=0; n<13; n++) I2C_BUS.writeByteSync(I2C_ADDRESS_SH1107, 0x40, 0x00); } // ================================================================================ diff --git a/modules/SHT31D.js b/modules/SHT31D.js index 6f1884d..7385b6e 100644 --- a/modules/SHT31D.js +++ b/modules/SHT31D.js @@ -8,6 +8,7 @@ const SGP30 = require('./SGP30.js'); const SH1107 = require('./SH1107.js'); const IS31FL3731_RGB = require('./IS31FL3731_RGB.js'); const IS31FL3731_WHITE = require('./IS31FL3731_WHITE.js'); +const HT16K33 = require('./HT16K33.js'); const { performance } = require('perf_hooks'); function Wait(msecs) { const start = performance.now(); while(performance.now() - start < msecs); } // Helper function @@ -173,7 +174,15 @@ function Display(refreshAll) { // ### PLACEHOLDER: DISPLAYS HUMIDITY AS SIMPLE TWO-DIGIT VALUE ### IS31FL3731_WHITE.DrawString(Math.round(data[0]).toString()); - } + } + + // ==================== + + if (HT16K33.IsAvailable()) + { + var humidity = Math.round(data[0]) + '%'; + HT16K33.Display(humidity); + } // ==================== diff --git a/modules/System.js b/modules/System.js index 3e918d6..40eb551 100644 --- a/modules/System.js +++ b/modules/System.js @@ -9,6 +9,7 @@ const { execSync } = require('child_process'); const SH1107 = require('./SH1107.js'); const IS31FL3731_RGB = require('./IS31FL3731_RGB.js'); const IS31FL3731_WHITE = require('./IS31FL3731_WHITE.js'); +const HT16K33 = require('./HT16K33.js'); module.exports = { Start: Start, @@ -230,7 +231,15 @@ function DisplayLoad(refreshAll) // ...and a meter showing the relative memory usage... IS31FL3731_WHITE.DrawMeter(memoryUsageInPercent, 6); } - } + } + + // ==================== + + if (HT16K33.IsAvailable()) + { + var loadAllCores = Math.round(cpuLoad.AllCores).toString() + '%'; + HT16K33.Display(loadAllCores); + } // ==================== @@ -321,7 +330,15 @@ function DisplayInfo(refreshAll) 20,40,40,40,20,20,20,40,40,20,20, 20,20,20,20,20,20,20,20,20,20,20 ]; IS31FL3731_WHITE.Display(pictBG); - } + } + + // ==================== + + if (HT16K33.IsAvailable()) + { + var loadAllCores = Math.round(cpuLoad.AllCores).toString() + '%'; + HT16K33.Display(loadAllCores); + } // ==================== diff --git a/modules/TCS3472.js b/modules/TCS3472.js index 8c38e12..21e5edf 100644 --- a/modules/TCS3472.js +++ b/modules/TCS3472.js @@ -8,6 +8,7 @@ const { execSync } = require('child_process'); const SH1107 = require('./SH1107.js'); const IS31FL3731_RGB = require('./IS31FL3731_RGB.js'); const IS31FL3731_WHITE = require('./IS31FL3731_WHITE.js'); +const HT16K33 = require('./HT16K33.js'); module.exports = { Identify: Identify, @@ -206,8 +207,15 @@ function Display(refreshAll) if (IS31FL3731_WHITE.IsAvailable()) { - IS31FL3731_WHITE.DrawString("#"); - } + IS31FL3731_WHITE.DrawString("#"); // PLACEHOLDER + } + + // ==================== + + if (HT16K33.IsAvailable()) + { + HT16K33.Display("####"); // PLACEHOLDER + } // ==================== diff --git a/modules/VCNL4010.js b/modules/VCNL4010.js index 04951d8..2793bc7 100644 --- a/modules/VCNL4010.js +++ b/modules/VCNL4010.js @@ -7,6 +7,7 @@ const i2c = require('i2c-bus'); // -> https://github.com/fivdi/i2c-bus const SH1107 = require('./SH1107.js'); const IS31FL3731_RGB = require('./IS31FL3731_RGB.js'); const IS31FL3731_WHITE = require('./IS31FL3731_WHITE.js'); +const HT16K33 = require('./HT16K33.js'); module.exports = { Identify: Identify, @@ -203,7 +204,18 @@ function Display(refreshAll) if (relativeProximity >= 0) img[66] = 30; IS31FL3731_WHITE.Display(img); - } + } + + // ==================== + + if (HT16K33.IsAvailable()) + { + var relativeProximity = Math.round(((data[1]-proximityOffset) * 999) / 16384); // Note: <...same rationale as above...> + if (data[1] > 16384) relativeProximity = 999; + + var proximity = '#' + relativeProximity.toString(); + HT16K33.Display(proximity); + } // ==================== diff --git a/modules/VEML6075.js b/modules/VEML6075.js index 6e9bb81..bc782cb 100644 --- a/modules/VEML6075.js +++ b/modules/VEML6075.js @@ -7,6 +7,7 @@ const i2c = require('i2c-bus'); // -> https://github.com/fivdi/i2c-bus const SH1107 = require('./SH1107.js'); const IS31FL3731_RGB = require('./IS31FL3731_RGB.js'); const IS31FL3731_WHITE = require('./IS31FL3731_WHITE.js'); +const HT16K33 = require('./HT16K33.js'); const DRV2605 = require('./DRV2605.js'); module.exports = { @@ -241,7 +242,15 @@ function Display(refreshAll) else if (data[3] == 3) IS31FL3731_WHITE.Display(pictHIG); // High else if (data[3] == 2) IS31FL3731_WHITE.Display(pictMOD); // Moderate else IS31FL3731_WHITE.Display(pictLOW); // Low - } + } + + // ==================== + + if (HT16K33.IsAvailable()) + { + var uvi = 'UVI' + data[3].toString(); + HT16K33.Display(uvi); + } // ====================