veml6070: add Vishay UV light sensor

This commit is contained in:
cn
2019-09-18 19:26:41 +02:00
committed by Ron Evans
parent 21b8d953f4
commit 955b3a56e8
5 changed files with 248 additions and 0 deletions
+1
View File
@@ -50,5 +50,6 @@ smoke-test:
tinygo build -size short -o ./build/test.elf -target=trinket-m0 ./examples/bme280/main.go
tinygo build -size short -o ./build/test.elf -target=circuitplay-express ./examples/microphone/main.go
tinygo build -size short -o ./build/test.elf -target=circuitplay-express ./examples/buzzer/main.go
tinygo build -size short -o ./build/test.elf -target=trinket-m0 ./examples/veml6070/main.go
test: clean fmt-check smoke-test
+1
View File
@@ -82,6 +82,7 @@ func main() {
| [ST7735 TFT color display](https://www.crystalfontz.com/controllers/Sitronix/ST7735R/319/) | SPI |
| [ST7789 TFT color display](https://cdn-shop.adafruit.com/product-files/3787/3787_tft_QT154H2201__________20190228182902.pdf) | SPI |
| [Thermistor](https://www.farnell.com/datasheets/33552.pdf) | ADC |
| [VEML6070 UV light sensor](https://www.vishay.com/docs/84277/veml6070.pdf) | I2C |
| [VL53L1X time-of-flight distance sensor](https://www.st.com/resource/en/datasheet/vl53l1x.pdf) | I2C |
| [Waveshare 2.13" e-paper display](https://www.waveshare.com/w/upload/e/e6/2.13inch_e-Paper_Datasheet.pdf) | SPI |
| [Waveshare 2.13" (B & C) e-paper display](https://www.waveshare.com/w/upload/d/d3/2.13inch-e-paper-b-Specification.pdf) | SPI |
+41
View File
@@ -0,0 +1,41 @@
package main
import (
"time"
"machine"
"tinygo.org/x/drivers/veml6070"
)
func main() {
machine.I2C0.Configure(machine.I2CConfig{})
sensor := veml6070.New(machine.I2C0)
if !sensor.Configure() {
println("VEML6070 could not be configured")
return
}
println("VEML6070 configured")
for {
intensity, _ := sensor.ReadUVALightIntensity()
println("UVA light intensity:", float32(intensity)/1000.0, "W/(m*m)")
switch sensor.GetEstimatedRiskLevel(intensity) {
case veml6070.UVI_RISK_LOW:
println("UV risk level: low")
case veml6070.UVI_RISK_MODERATE:
println("UV risk level: moderate")
case veml6070.UVI_RISK_HIGH:
println("UV risk level: high")
case veml6070.UVI_RISK_VERY_HIGH:
println("UV risk level: very high")
case veml6070.UVI_RISK_EXTREME:
println("UV risk level: extreme")
}
time.Sleep(2 * time.Second)
}
}
+65
View File
@@ -0,0 +1,65 @@
package veml6070
// I2C addresses and other constants
const (
ADDR_L = 0x38 // 7bit address of the VEML6070 (write, read)
ADDR_H = 0x39 // 7bit address of the VEML6070 (read)
)
// Some possible values for resistance value (in ohm) of VEML6070 calibration resistor
const (
RSET_240K = 240000
RSET_270K = 270000
RSET_300K = 300000
RSET_600K = 600000
)
// Possible values for integration time of VEML6070
// (internally represents the config register bit mask)
const (
IT_HALF = 0x00
IT_1 = 0x04
IT_2 = 0x08
IT_4 = 0x0C
)
// Possible values for UVI (UV index) risk level estimations - the VEML6070 can
// only estimate UVI risk levels since it can only sense UVA rays but the vendor
// tried to come up with some coarse thresholds, from application notes
const (
UVI_RISK_LOW = iota
UVI_RISK_MODERATE
UVI_RISK_HIGH
UVI_RISK_VERY_HIGH
UVI_RISK_EXTREME
)
// Scale factor in milliseconds / ohm to determine refresh time
// (aka sampling time) without IT_FACTOR for any given RSET, from datasheet.
// Note: 100.0 milliseconds are applicable for RSET=240 kOhm and IT_FACTOR=1
const RSET_TO_REFRESHTIME_SCALE = 100.0 / RSET_240K
// The refresh time in milliseconds for which NORMALIZED_UVA_SENSITIVITY
// is applicable to a step count
const NORMALIZED_REFRESHTIME = 100.0
// The UVA sensitivity in mW/(m*m)/step which is applicable to a step count
// normalized to the NORMALIZED_REFRESHTIME, from datasheet for RSET=240 kOhm
// and IT_FACTOR=1
const NORMALIZED_UVA_SENSITIVITY = 50.0
// Config register
// Possible values for shutdown
const (
CONFIG_SD_DISABLE = 0x00
CONFIG_SD_ENABLE = 0x01
)
// Enable / disable
const (
CONFIG_DEFAULTS = 0x02
CONFIG_ENABLE = CONFIG_SD_DISABLE | CONFIG_DEFAULTS
CONFIG_DISABLE = CONFIG_SD_ENABLE | CONFIG_DEFAULTS
)
+140
View File
@@ -0,0 +1,140 @@
// Package veml6070 provides a driver for the VEML6070 digital UV light sensor
// by Vishay.
//
// Datasheet:
// https://www.vishay.com/docs/84277/veml6070.pdf
// Application Notes:
// https://www.vishay.com/docs/84310/designingveml6070.pdf
//
package veml6070 // import "tinygo.org/x/drivers/veml6070"
import (
"time"
"machine"
)
// Device wraps an I2C connection to a VEML6070 device.
type Device struct {
bus machine.I2C
AddressLow uint16
AddressHigh uint16
RSET uint32
IT uint8
}
// New creates a new VEML6070 connection. The I2C bus must already be
// configured.
//
// This function only creates the Device object, it does not initialize the device.
// You must call Configure() first in order to use the device itself.
func New(bus machine.I2C) Device {
return Device{
bus: bus,
AddressLow: ADDR_L,
AddressHigh: ADDR_H,
RSET: RSET_240K,
// Note: default to maximum to get as much precision as possible since
// raw data values larger than 16 bit can hardly occur with RSET below
// 300 kOhm in real world applications. Power saving due to shorter
// sampling time might be a reason to reduce this.
IT: IT_4,
}
}
// Configure sets up the device for communication
func (d *Device) Configure() bool {
// save power by shutdown as early as possible, also serves as presence test
if err := d.disable(); err != nil {
return false
}
return true
}
// ReadUVALightIntensity returns the UVA light intensity (irradiance)
// in milli Watt per square meter (mW/(m*m))
func (d *Device) ReadUVALightIntensity() (uint32, error) {
var err2 error
if err := d.enable(); err != nil {
return 0, err
}
// wait two times the refresh time to allow completion of a previous cycle
// with old settings (worst case)
time.Sleep(time.Duration(d.getRefreshTime()) * 2 * time.Millisecond)
msb, err2 := d.readData(d.AddressHigh)
if err2 != nil {
return 0, err2
}
lsb, err2 := d.readData(d.AddressLow)
if err2 != nil {
return 0, err2
}
if err := d.disable(); err != nil {
return 0, err
}
rawData := (uint32(msb) << 8) | uint32(lsb)
// normalize raw data (step count sampled in d.getRefreshTime()) into the
// linearly scaled normalized data (step count sampled in 100ms) for which
// we know the UVA sensitivity
normalizedData := float32(rawData) * NORMALIZED_REFRESHTIME / d.getRefreshTime()
// now we can calculate the absolute UVA power detected combining normalized
// data with known UVA sensitivity for this data, from datasheet
intensity := normalizedData * NORMALIZED_UVA_SENSITIVITY // mW/(m*m)
return uint32(intensity + 0.5), nil
}
// GetEstimatedRiskLevel returns estimated risk level from comparing UVA light
// intensity values in mW/(m*m) with thresholds calculated from application notes
func (d *Device) GetEstimatedRiskLevel(intensity uint32) uint8 {
if intensity <= 24888 {
return UVI_RISK_LOW
} else if intensity <= 49800 {
return UVI_RISK_MODERATE
} else if intensity <= 66400 {
return UVI_RISK_HIGH
} else if intensity <= 91288 {
return UVI_RISK_VERY_HIGH
} else {
return UVI_RISK_EXTREME
}
}
func (d *Device) disable() error {
return d.bus.Tx(uint16(d.AddressLow), []byte{CONFIG_DISABLE}, nil)
}
func (d *Device) enable() error {
return d.bus.Tx(uint16(d.AddressLow), []byte{CONFIG_ENABLE | d.IT}, nil)
}
func (d *Device) readData(address uint16) (byte, error) {
data := []byte{0}
err := machine.I2C0.Tx(address, []byte{}, data)
return data[0], err
}
// getRefreshTime returns the refresh time (aka sample time) in milliseconds
func (d *Device) getRefreshTime() float32 {
var it float32
switch d.IT {
case IT_HALF:
it = 0.5
case IT_1:
it = 1
case IT_2:
it = 2
case IT_4:
it = 4
}
return float32(d.RSET) * RSET_TO_REFRESHTIME_SCALE * it
}