This commit is contained in:
choelzl 2022-03-28 00:42:48 +02:00
commit 3e858fa48e
Signed by: sora
GPG Key ID: A362EA0491E2EEA0
56 changed files with 62251 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
env.h

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
doc/BME280_datasheet.pdf Normal file

Binary file not shown.

BIN
doc/ESP32_One_Sch.pdf Normal file

Binary file not shown.

Binary file not shown.

BIN
doc/GT911_Datasheet.pdf Normal file

Binary file not shown.

BIN
doc/ICM-20948-v1.3.pdf Normal file

Binary file not shown.

BIN
doc/PMSA003I_series.pdf Normal file

Binary file not shown.

Binary file not shown.

BIN
doc/TSL2591.pdf Normal file

Binary file not shown.

BIN
doc/esp32_datasheet_en.pdf Normal file

Binary file not shown.

File diff suppressed because one or more lines are too long

335
metesp.ino Normal file
View File

@ -0,0 +1,335 @@
/* Includes ------------------------------------------------------------------*/
#include "env.h"
#include "src/epd/epd_cfg.h"
#include "src/epd/epd_driver.h"
#include "src/epd/epd_paint.h"
#include "src/ntpt/ntp.h"
#include "src/owm/owm.h"
#include "src/sensor/sensor.h"
#include "src/touch/gt1151.h"
#include "src/influxdb/influx.h"
#include <Arduino.h>
#include <Wire.h>
#include <stdlib.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#define PIN_SDA 18
#define PIN_SCL 23
/* Global Variables -----------------------------------------------------------*/
typedef enum {
VIEW_MAIN = 0,
VIEW_WEATHER, VIEW_WEATHER_ALT,
VIEW_TEMP, VIEW_AIR,
VIEW_CUSTOM,
VIEW_COUNT
} views_t;
static const uint8_t view_count = 5;
static uint8_t view_curr;
static const views_t view_tab[view_count] = {
VIEW_MAIN,
VIEW_WEATHER,VIEW_WEATHER_ALT,
VIEW_TEMP,VIEW_AIR
};
static WiFiMulti wifiMulti;
static TwoWire I2C = TwoWire(0);
static strDateTime dateTime = {0};
static Sensor* sensor = new Sensor(&I2C);
static GT1151* touch = new GT1151(&I2C);
static OWM* owm = new OWM(LOCATION, OPENWEATHER_API);
static OWM* owm_alt = new OWM(LOCATION_ALT, OPENWEATHER_API);
static Influx* iflx = new Influx(NULL, owm, owm_alt);//add sensor
static NTPtime NTPch(NTP_URL);
static bool time_update = false,
ntp_update = true,
weather_update = true,
sensor_update = true,
influx_w_update = true, influx_s_update = true,
view_update = true, view_refresh = false;
static bool shutdownt = false;
static int shutdownc = 0;
uint8_t *BlackImage;
/* IRQ ------------------------------------------------------------------------*/
hw_timer_t * timer = NULL;
void IRAM_ATTR onSecondTimer() {
dateTime.second += 1;
if (dateTime.second == 60) {
dateTime.minute += 1;
dateTime.second = 0;
if (dateTime.minute == 60) {
dateTime.hour += 1;
dateTime.minute = 0;
if (dateTime.hour == 24) {
dateTime.hour = 0;
dateTime.minute = 0;
dateTime.second = 0;
}
ntp_update = true;
}
if(dateTime.minute % 5) weather_update = true;
if(dateTime.minute % 5) sensor_update = true;
if(dateTime.minute % 5) influx_w_update = true;
if(dateTime.minute % 5) influx_s_update = true;
view_update = true;
time_update = true;
}
if(touch->dev.holding) shutdownc++;
else shutdownc = 0;
}
void IRAM_ATTR onTouch(int8_t contacts, GTPoint *points) {
if(touch->dev.holding) return;
Serial.printf("Contacts: %d\n", contacts);
for (uint8_t i = 0; i < contacts; i++) {
Serial.printf("C%d: #%d %d,%d s:%d\n", i, points[i].id, points[i].x, points[i].y, points[i].a);
yield();
}
if(points[0].x < 125) view_curr = (++view_curr % view_count);
else view_curr = (--view_curr % view_count);
view_update = true;
}
//x = 250 to 0
//y = ????
/* Setup Functions -----------------------------------------------------------*/
void setup_EPD(){
//START EPD
DEV_Module_Init();
EPD_2IN13_V2_Init(EPD_2IN13_V2_FULL);
EPD_2IN13_V2_Clear();
delay(100);
//EPD STORAGE
uint16_t Imagesize = ((EPD_2IN13_V2_WIDTH % 8 == 0) ? (EPD_2IN13_V2_WIDTH / 8 ) : (EPD_2IN13_V2_WIDTH / 8 + 1)) * EPD_2IN13_V2_HEIGHT;
if ((BlackImage = (uint8_t *)malloc(Imagesize)) == NULL) exit(1);
Paint_NewImage(BlackImage, EPD_2IN13_V2_WIDTH, EPD_2IN13_V2_HEIGHT, 270, WHITE);
Paint_SelectImage(BlackImage);
Paint_SetMirroring(MIRROR_HORIZONTAL);
Paint_Clear(WHITE);
Paint_DrawIcon(89,54, ICON_LOGO, &FontIcon, BLACK, WHITE);
Paint_DrawString_EN(29, 5, "Helcel", &Font45, WHITE, BLACK);
Paint_SelectImage(BlackImage);
EPD_2IN13_V2_Display(BlackImage);
delay(100);
}
void setup_EPD_END(){
Paint_Clear(WHITE);
EPD_2IN13_V2_Init(EPD_2IN13_V2_FULL);
EPD_2IN13_V2_DisplayPartBaseImage(BlackImage);
EPD_2IN13_V2_Init(EPD_2IN13_V2_PART);
Paint_SelectImage(BlackImage);
}
void setup_TOUCH() {
touch->setHandler(onTouch);
touch->begin();
}
void setup_WIFI(){
uint8_t tries = 3;
WIFI_REGISTER_AP(wifiMulti);
while (wifiMulti.run() == WL_CONNECTED && tries-- >0) delay(500);
}
void setup_TIMER(){
timer = timerBegin(0, 80, true);
timerAttachInterrupt(timer, &onSecondTimer, true);
timerAlarmWrite(timer, 1000000, true);
timerAlarmEnable(timer);
}
/* Setup Functions --------------------------------------------------------------*/
void setup(){
Serial.begin(115200);
I2C.begin(PIN_SDA, PIN_SCL, 100000);
I2C.setPins(PIN_SDA,PIN_SCL);
setup_EPD();
setup_TOUCH();
setup_WIFI();
setup_TIMER();
iflx->check();
setup_EPD_END();
}
/* Update Functions-------------------------------------------------------------*/
void updateNTP(){
if (!ntp_update) return;
if (wifiMulti.run() != WL_CONNECTED) return;
strDateTime nDateTime = NTPch.getNTPtime(1.0, 1);
if(nDateTime.valid){
dateTime = nDateTime;
time_update = true;
ntp_update = false;
}
}
void updateWeather(){
if (!weather_update) return;
if (wifiMulti.run() != WL_CONNECTED) return;
if(owm!= NULL && !owm->update()) return;
if(owm_alt!= NULL && !owm_alt->update()) return;
weather_update = false;
}
void updateTime(){
if (!time_update) return;
Paint_ClearWindows(170, EPD_2IN13_V2_WIDTH - Font24.Height, 170 + Font24.Width * 4.5, EPD_2IN13_V2_WIDTH, WHITE);
if (!dateTime.valid) return;
Paint_DrawTime(170, 98, &dateTime, &Font24, WHITE, BLACK);
EPD_2IN13_V2_DisplayPart(BlackImage);
time_update = false;
}
void updateSensor(){
if (!sensor_update) return;
sensor->measure();
sensor_update = false;
}
ICON_tpe getWeatherIcon(int id){
if(id>=200 && id<300){ //Thunder
return ICON_CLOUD_THUNDER;
}else if(id>=300 && id<400){//Drizzle
return ICON_CLOUD_RAIN;
}else if(id>=500 && id<600){//Rain
return ICON_CLOUD_RAIN;
}else if(id>=600 && id<700){//Snow
return ICON_CLOUD_SNOW;
}else if(id>=700 && id<800){//Warning
return ICON_WARN;
}else if(id>=800 && id<900){//Clear & Cloudy
if(id==800) return ICON_SUN;
else if(id==801) return ICON_CLOUD_SUN;
else return ICON_CLOUD;
}else{
return ICON_WARN;
}
}
void refreshDisplay(){
Paint_Clear(WHITE);
EPD_2IN13_V2_Init(EPD_2IN13_V2_FULL);
EPD_2IN13_V2_DisplayPartBaseImage(BlackImage);
EPD_2IN13_V2_Init(EPD_2IN13_V2_PART);
Paint_SelectImage(BlackImage);
}
void updateDisplay(){
// forceRefresh();
if (!view_update) return;
if (view_refresh) refreshDisplay();
Paint_ClearWindows(0, 0, EPD_2IN13_V2_HEIGHT, EPD_2IN13_V2_WIDTH - Font24.Height, WHITE);
Paint_ClearWindows(0, EPD_2IN13_V2_WIDTH - Font24.Height, 170, EPD_2IN13_V2_WIDTH, WHITE);
switch(view_tab[view_curr]){
case VIEW_MAIN:
Paint_DrawIcon(0,0, ICON_LOGO,&FontIcon, BLACK, WHITE);
Paint_DrawString_EN(60,0,"MAIN", &Font24, WHITE, BLACK);
Paint_DrawIntUnit(0,30, 69,"", &Font24, BLACK, WHITE);
break;
case VIEW_WEATHER_ALT:
case VIEW_WEATHER:
{
OWM* cowm = (view_tab[view_curr]==VIEW_WEATHER)?owm:owm_alt;
if(!cowm->valid_weather) break;
JsonObject weather_0 = cowm->weather["weather"][0];
JsonObject weather_main = cowm->weather["main"];
JsonObject weather_wind = cowm->weather["wind"];
JsonObject weather_sys = cowm->weather["sys"];
if(!weather_0 || !weather_main || !weather_sys || !weather_wind) break;
Paint_DrawIcon(0,8, getWeatherIcon(weather_0["id"]), &FontIcon, BLACK, WHITE);
Paint_DrawString_EN(2,EPD_2IN13_V2_WIDTH - 4 - Font16.Height,weather_0["description"], &Font16, WHITE, BLACK);
Paint_DrawFltUnit(76,0,weather_main["temp"], "C", &Font24, BLACK, WHITE);
Paint_DrawFltUnit(186,6,weather_main["feels_like"], "C", &Font12, BLACK, WHITE);
Paint_DrawIntUnit(76,24,weather_main["pressure"], "hPa", &Font24, BLACK, WHITE);
Paint_DrawIntUnit(76,48,weather_main["humidity"], "%", &Font24, BLACK, WHITE);
Paint_DrawFltUnit(76,72,weather_wind["speed"], "m/s", &Font24, BLACK, WHITE);
strDateTime sunrisedt = NTPtime::ConvertUnixTimestamp(NTPtime::adjustTimeZone(weather_sys["sunrise"], 1.0, 1));
strDateTime sunsetdt = NTPtime::ConvertUnixTimestamp(NTPtime::adjustTimeZone(weather_sys["sunset"], 1.0, 1));
Paint_DrawTime(20, 6, &sunrisedt, &Font12, WHITE, BLACK);
Paint_DrawTime(20, 76, &sunsetdt, &Font12, WHITE, BLACK);
}
break;
case VIEW_TEMP:
Paint_DrawIcon(0,Font24.Height, ICON_TEMPERATURE,&FontIcon, BLACK, WHITE);
Paint_DrawString_EN(2,0,"Temp", &Font24, WHITE, BLACK);
Paint_DrawFltUnit(76,0,sensor->heatidx, "C", &Font24, BLACK, WHITE);
Paint_DrawFltUnit(76,36,sensor->temp, "C", &Font24, BLACK, WHITE);
Paint_DrawFltUnit(160,36,sensor->hum, "%", &Font24, BLACK, WHITE);
Paint_DrawFltUnit(76,64,sensor->pres, "hPa", &Font24, BLACK, WHITE);
break;
case VIEW_AIR:
Paint_DrawIcon(0,Font24.Height, ICON_POLUTION,&FontIcon, BLACK, WHITE);
Paint_DrawString_EN(2,0,"Air", &Font24, WHITE, BLACK);
Paint_DrawIntUnit(150,10, 69,"", &Font24, BLACK, WHITE);
break;
case VIEW_CUSTOM:
Paint_DrawString_EN(60,0,"CUSTOM", &Font24, WHITE, BLACK);
break;
default:
break;
}
EPD_2IN13_V2_DisplayPart(BlackImage);
view_update = false;
}
void updateInflux(){
if(wifiMulti.run() != WL_CONNECTED) return;
iflx->record(influx_w_update, influx_s_update);
influx_w_update = false;
influx_s_update = false;
}
/* The main loop -------------------------------------------------------------*/
void loop(){
if(shutdownc >= 10){
refreshDisplay();
EPD_2IN13_V2_Sleep();
esp_deep_sleep_start();
}
updateNTP();
updateWeather();
updateSensor();
updateInflux();
touch->update();
updateDisplay();
updateTime();
delay(50);
if(!touch->dev.holding) shutdownc = 0;
}

38
src/epd/convert.js Normal file
View File

@ -0,0 +1,38 @@
#!/bin/node
const fs = require('fs')
const fileContents = fs.readFileSync('./icon.h').toString()
let arr = fileContents.split('static unsigned char header_data[] = {');
let imgcode = arr[1].replace('};','').replace('\n','').replace('\t','')
let str = imgcode.split(',')
let charWidth = 32
let output = []
let cur = 0;
let idx = 0;
let line = 0;
for(let e of str){
if(idx >=8){
idx = 0;
output.push('0x'+cur.toString(16)+',')
++line;
cur = 0;
if(line >= 9){
output.push('\n');
line = 0;
}
}
if(e == 0){
cur |= (0x1 << (7-idx));
}
++idx;
}
idx = 0;
output.push('0x'+cur.toString(16)+',')
console.log(output.join(''))

13
src/epd/debug.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef __DEBUG_H
#define __DEBUG_H
#include <Wire.h>
#define USE_DEBUG 0
#if USE_DEBUG
#define Debug(__info) Serial.print(__info)
#else
#define Debug(__info)
#endif
#endif

46
src/epd/epd_cfg.cpp Normal file
View File

@ -0,0 +1,46 @@
#include "epd_cfg.h"
void GPIO_Config(void){
pinMode(EPD_BUSY_PIN, INPUT);
pinMode(EPD_RST_PIN , OUTPUT);
pinMode(EPD_DC_PIN , OUTPUT);
pinMode(EPD_SCK_PIN, OUTPUT);
pinMode(EPD_MOSI_PIN, OUTPUT);
pinMode(EPD_CS_PIN , OUTPUT);
digitalWrite(EPD_CS_PIN , HIGH);
digitalWrite(EPD_SCK_PIN, LOW);
}
/******************************************************************************
function: Module Initialize, the BCM2835 library and initialize the pins, SPI protocol
parameter:
Info:
******************************************************************************/
uint8_t DEV_Module_Init(void){
GPIO_Config();
#ifdef USE_DEBUG
Serial.begin(115200);
#endif
return 0;
}
/******************************************************************************
function:
SPI read and write
******************************************************************************/
void DEV_SPI_WriteByte(uint8_t data){
digitalWrite(EPD_CS_PIN, GPIO_PIN_RESET);
for (int i = 0; i < 8; i++){
if ((data & 0x80) == 0) digitalWrite(EPD_MOSI_PIN, GPIO_PIN_RESET);
else digitalWrite(EPD_MOSI_PIN, GPIO_PIN_SET);
data <<= 1;
digitalWrite(EPD_SCK_PIN, GPIO_PIN_SET);
digitalWrite(EPD_SCK_PIN, GPIO_PIN_RESET);
}
digitalWrite(EPD_CS_PIN, GPIO_PIN_SET);
}

33
src/epd/epd_cfg.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef _EPD_CONFIG_H_
#define _DEV_CONFIG_H_
#define _EPD_CONFIG_H_
#include <Arduino.h>
#include <stdint.h>
#include <stdio.h>
/**
* GPIO config
**/
#define EPD_SCK_PIN 14
#define EPD_MOSI_PIN 13
#define EPD_CS_PIN 5
#define EPD_RST_PIN 19
#define EPD_DC_PIN 0
#define EPD_BUSY_PIN 4
#define GPIO_PIN_SET 1
#define GPIO_PIN_RESET 0
/**
* GPIO read and write
**/
#define DEV_Digital_Write(_pin, _value) digitalWrite(_pin, _value == 0? LOW:HIGH)
#define DEV_Digital_Read(_pin) digitalRead(_pin)
/*------------------------------------------------------------------------------------------------------*/
uint8_t DEV_Module_Init(void);
void DEV_SPI_WriteByte(uint8_t data);
#endif

352
src/epd/epd_driver.cpp Normal file
View File

@ -0,0 +1,352 @@
/*****************************************************************************
* | File : EPD_2in13_V2.c
* | Author : Waveshare team
* | Function : 2.13inch e-paper V2
* | Info :
*----------------
* | This version: V3.0
* | Date : 2019-06-13
* | Info :
* -----------------------------------------------------------------------------
* V3.0(2019-06-13):
* 1.Change name:
* EPD_Reset() => EPD_2IN13_V2_Reset()
* EPD_SendCommand() => EPD_2IN13_V2_SendCommand()
* EPD_SendData() => EPD_2IN13_V2_SendData()
* EPD_WaitUntilIdle() => EPD_2IN13_V2_ReadBusy()
* EPD_Init() => EPD_2IN13_V2_Init()
* EPD_Clear() => EPD_2IN13_V2_Clear()
* EPD_Display() => EPD_2IN13_V2_Display()
* EPD_Sleep() => EPD_2IN13_V2_Sleep()
* 2.add:
* EPD_2IN13_V2_DisplayPartBaseImage()
* -----------------------------------------------------------------------------
* V2.0(2018-11-14):
* 1.Remove:ImageBuff[EPD_HEIGHT * EPD_WIDTH / 8]
* 2.Change:EPD_2IN13_V2_Display(uint8_t *Image)
* Need to pass parameters: pointer to cached data
* 3.Change:
* EPD_RST -> EPD_RST_PIN
* EPD_DC -> EPD_DC_PIN
* EPD_CS -> EPD_CS_PIN
* EPD_BUSY -> EPD_BUSY_PIN
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documnetation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
******************************************************************************/
#include "epd_driver.h"
#include "debug.h"
const unsigned char EPD_2IN13_V2_lut_full_update[]= {
0x80,0x60,0x40,0x00,0x00,0x00,0x00, //LUT0: BB: VS 0 ~7
0x10,0x60,0x20,0x00,0x00,0x00,0x00, //LUT1: BW: VS 0 ~7
0x80,0x60,0x40,0x00,0x00,0x00,0x00, //LUT2: WB: VS 0 ~7
0x10,0x60,0x20,0x00,0x00,0x00,0x00, //LUT3: WW: VS 0 ~7
0x00,0x00,0x00,0x00,0x00,0x00,0x00, //LUT4: VCOM: VS 0 ~7
0x03,0x03,0x00,0x00,0x02, // TP0 A~D RP0
0x09,0x09,0x00,0x00,0x02, // TP1 A~D RP1
0x03,0x03,0x00,0x00,0x02, // TP2 A~D RP2
0x00,0x00,0x00,0x00,0x00, // TP3 A~D RP3
0x00,0x00,0x00,0x00,0x00, // TP4 A~D RP4
0x00,0x00,0x00,0x00,0x00, // TP5 A~D RP5
0x00,0x00,0x00,0x00,0x00, // TP6 A~D RP6
0x15,0x41,0xA8,0x32,0x30,0x0A,
};
const unsigned char EPD_2IN13_V2_lut_partial_update[]= { //20 bytes
0x00,0x00,0x00,0x00,0x00,0x00,0x00, //LUT0: BB: VS 0 ~7
0x80,0x00,0x00,0x00,0x00,0x00,0x00, //LUT1: BW: VS 0 ~7
0x40,0x00,0x00,0x00,0x00,0x00,0x00, //LUT2: WB: VS 0 ~7
0x00,0x00,0x00,0x00,0x00,0x00,0x00, //LUT3: WW: VS 0 ~7
0x00,0x00,0x00,0x00,0x00,0x00,0x00, //LUT4: VCOM: VS 0 ~7
0x0A,0x00,0x00,0x00,0x00, // TP0 A~D RP0
0x00,0x00,0x00,0x00,0x00, // TP1 A~D RP1
0x00,0x00,0x00,0x00,0x00, // TP2 A~D RP2
0x00,0x00,0x00,0x00,0x00, // TP3 A~D RP3
0x00,0x00,0x00,0x00,0x00, // TP4 A~D RP4
0x00,0x00,0x00,0x00,0x00, // TP5 A~D RP5
0x00,0x00,0x00,0x00,0x00, // TP6 A~D RP6
0x15,0x41,0xA8,0x32,0x30,0x0A,
};
/******************************************************************************
function : Software reset
parameter:
******************************************************************************/
static void EPD_2IN13_V2_Reset(void){
DEV_Digital_Write(EPD_RST_PIN, 1);
delay(200);
DEV_Digital_Write(EPD_RST_PIN, 0);
delay(10);
DEV_Digital_Write(EPD_RST_PIN, 1);
delay(200);
}
/******************************************************************************
function : send command
parameter:
Reg : Command register
******************************************************************************/
static void EPD_2IN13_V2_SendCommand(uint8_t Reg){
DEV_Digital_Write(EPD_DC_PIN, 0);
DEV_Digital_Write(EPD_CS_PIN, 0);
DEV_SPI_WriteByte(Reg);
DEV_Digital_Write(EPD_CS_PIN, 1);
}
/******************************************************************************
function : send data
parameter:
Data : Write data
******************************************************************************/
static void EPD_2IN13_V2_SendData(uint8_t Data){
DEV_Digital_Write(EPD_DC_PIN, 1);
DEV_Digital_Write(EPD_CS_PIN, 0);
DEV_SPI_WriteByte(Data);
DEV_Digital_Write(EPD_CS_PIN, 1);
}
/******************************************************************************
function : Wait until the busy_pin goes LOW
parameter:
******************************************************************************/
void EPD_2IN13_V2_ReadBusy(void){
Debug("e-Paper busy\r\n");
while(DEV_Digital_Read(EPD_BUSY_PIN) == 1) { //LOW: idle, HIGH: busy
delay(100);
}
Debug("e-Paper busy release\r\n");
}
/******************************************************************************
function : Turn On Display
parameter:
******************************************************************************/
static void EPD_2IN13_V2_TurnOnDisplay(void){
EPD_2IN13_V2_SendCommand(0x22);
EPD_2IN13_V2_SendData(0xC7);
EPD_2IN13_V2_SendCommand(0x20);
EPD_2IN13_V2_ReadBusy();
}
/******************************************************************************
function : Turn On Display
parameter:
******************************************************************************/
static void EPD_2IN13_V2_TurnOnDisplayPart(void){
EPD_2IN13_V2_SendCommand(0x22);
EPD_2IN13_V2_SendData(0x0C);
EPD_2IN13_V2_SendCommand(0x20);
EPD_2IN13_V2_ReadBusy();
}
/******************************************************************************
function : Initialize the e-Paper register
parameter:
******************************************************************************/
void EPD_2IN13_V2_Init(uint8_t Mode){
uint8_t count;
EPD_2IN13_V2_Reset();
if(Mode == EPD_2IN13_V2_FULL) {
EPD_2IN13_V2_ReadBusy();
EPD_2IN13_V2_SendCommand(0x12); // soft reset
EPD_2IN13_V2_ReadBusy();
EPD_2IN13_V2_SendCommand(0x74); //set analog block control
EPD_2IN13_V2_SendData(0x54);
EPD_2IN13_V2_SendCommand(0x7E); //set digital block control
EPD_2IN13_V2_SendData(0x3B);
EPD_2IN13_V2_SendCommand(0x01); //Driver output control
EPD_2IN13_V2_SendData(0xF9);
EPD_2IN13_V2_SendData(0x00);
EPD_2IN13_V2_SendData(0x00);
EPD_2IN13_V2_SendCommand(0x11); //data entry mode
EPD_2IN13_V2_SendData(0x01);
EPD_2IN13_V2_SendCommand(0x44); //set Ram-X address start/end position
EPD_2IN13_V2_SendData(0x00);
EPD_2IN13_V2_SendData(0x0F); //0x0C-->(15+1)*8=128
EPD_2IN13_V2_SendCommand(0x45); //set Ram-Y address start/end position
EPD_2IN13_V2_SendData(0xF9); //0xF9-->(249+1)=250
EPD_2IN13_V2_SendData(0x00);
EPD_2IN13_V2_SendData(0x00);
EPD_2IN13_V2_SendData(0x00);
EPD_2IN13_V2_SendCommand(0x3C); //BorderWavefrom
EPD_2IN13_V2_SendData(0x03);
EPD_2IN13_V2_SendCommand(0x2C); //VCOM Voltage
EPD_2IN13_V2_SendData(0x55); //
EPD_2IN13_V2_SendCommand(0x03);
EPD_2IN13_V2_SendData(EPD_2IN13_V2_lut_full_update[70]);
EPD_2IN13_V2_SendCommand(0x04); //
EPD_2IN13_V2_SendData(EPD_2IN13_V2_lut_full_update[71]);
EPD_2IN13_V2_SendData(EPD_2IN13_V2_lut_full_update[72]);
EPD_2IN13_V2_SendData(EPD_2IN13_V2_lut_full_update[73]);
EPD_2IN13_V2_SendCommand(0x3A); //Dummy Line
EPD_2IN13_V2_SendData(EPD_2IN13_V2_lut_full_update[74]);
EPD_2IN13_V2_SendCommand(0x3B); //Gate time
EPD_2IN13_V2_SendData(EPD_2IN13_V2_lut_full_update[75]);
EPD_2IN13_V2_SendCommand(0x32);
for(count = 0; count < 70; count++) {
EPD_2IN13_V2_SendData(EPD_2IN13_V2_lut_full_update[count]);
}
EPD_2IN13_V2_SendCommand(0x4E); // set RAM x address count to 0;
EPD_2IN13_V2_SendData(0x00);
EPD_2IN13_V2_SendCommand(0x4F); // set RAM y address count to 0X127;
EPD_2IN13_V2_SendData(0xF9);
EPD_2IN13_V2_SendData(0x00);
EPD_2IN13_V2_ReadBusy();
} else if(Mode == EPD_2IN13_V2_PART) {
EPD_2IN13_V2_SendCommand(0x2C); //VCOM Voltage
EPD_2IN13_V2_SendData(0x26);
EPD_2IN13_V2_ReadBusy();
EPD_2IN13_V2_SendCommand(0x32);
for(count = 0; count < 70; count++) {
EPD_2IN13_V2_SendData(EPD_2IN13_V2_lut_partial_update[count]);
}
EPD_2IN13_V2_SendCommand(0x37);
EPD_2IN13_V2_SendData(0x00);
EPD_2IN13_V2_SendData(0x00);
EPD_2IN13_V2_SendData(0x00);
EPD_2IN13_V2_SendData(0x00);
EPD_2IN13_V2_SendData(0x40);
EPD_2IN13_V2_SendData(0x00);
EPD_2IN13_V2_SendData(0x00);
EPD_2IN13_V2_SendCommand(0x22);
EPD_2IN13_V2_SendData(0xC0);
EPD_2IN13_V2_SendCommand(0x20);
EPD_2IN13_V2_ReadBusy();
EPD_2IN13_V2_SendCommand(0x3C); //BorderWavefrom
EPD_2IN13_V2_SendData(0x01);
} else {
Debug("error, the Mode is EPD_2IN13_FULL or EPD_2IN13_PART");
}
}
/******************************************************************************
function : Clear screen
parameter:
******************************************************************************/
void EPD_2IN13_V2_Clear(void){
uint16_t Width, Height;
Width = (EPD_2IN13_V2_WIDTH % 8 == 0)? (EPD_2IN13_V2_WIDTH / 8 ): (EPD_2IN13_V2_WIDTH / 8 + 1);
Height = EPD_2IN13_V2_HEIGHT;
EPD_2IN13_V2_SendCommand(0x24);
for (uint16_t j = 0; j < Height; j++) {
for (uint16_t i = 0; i < Width; i++) {
EPD_2IN13_V2_SendData(0XFF);
}
}
EPD_2IN13_V2_TurnOnDisplay();
}
/******************************************************************************
function : Sends the image buffer in RAM to e-Paper and displays
parameter:
******************************************************************************/
void EPD_2IN13_V2_Display(uint8_t *Image){
uint16_t Width, Height;
Width = (EPD_2IN13_V2_WIDTH % 8 == 0)? (EPD_2IN13_V2_WIDTH / 8 ): (EPD_2IN13_V2_WIDTH / 8 + 1);
Height = EPD_2IN13_V2_HEIGHT;
EPD_2IN13_V2_SendCommand(0x24);
for (uint16_t j = 0; j < Height; j++) {
for (uint16_t i = 0; i < Width; i++) {
EPD_2IN13_V2_SendData(Image[i + j * Width]);
}
}
EPD_2IN13_V2_TurnOnDisplay();
}
/******************************************************************************
function : The image of the previous frame must be uploaded, otherwise the
first few seconds will display an exception.
parameter:
******************************************************************************/
void EPD_2IN13_V2_DisplayPartBaseImage(uint8_t *Image){
uint16_t Width, Height;
Width = (EPD_2IN13_V2_WIDTH % 8 == 0)? (EPD_2IN13_V2_WIDTH / 8 ): (EPD_2IN13_V2_WIDTH / 8 + 1);
Height = EPD_2IN13_V2_HEIGHT;
uint32_t Addr = 0;
EPD_2IN13_V2_SendCommand(0x24);
for (uint16_t j = 0; j < Height; j++) {
for (uint16_t i = 0; i < Width; i++) {
Addr = i + j * Width;
EPD_2IN13_V2_SendData(Image[Addr]);
}
}
EPD_2IN13_V2_SendCommand(0x26);
for (uint16_t j = 0; j < Height; j++) {
for (uint16_t i = 0; i < Width; i++) {
Addr = i + j * Width;
EPD_2IN13_V2_SendData(Image[Addr]);
}
}
EPD_2IN13_V2_TurnOnDisplay();
}
void EPD_2IN13_V2_DisplayPart(uint8_t *Image){
uint16_t Width, Height;
Width = (EPD_2IN13_V2_WIDTH % 8 == 0)? (EPD_2IN13_V2_WIDTH / 8 ): (EPD_2IN13_V2_WIDTH / 8 + 1);
Height = EPD_2IN13_V2_HEIGHT;
EPD_2IN13_V2_SendCommand(0x24);
for (uint16_t j = 0; j < Height; j++) {
for (uint16_t i = 0; i < Width; i++) {
EPD_2IN13_V2_SendData(Image[i + j * Width]);
}
}
EPD_2IN13_V2_TurnOnDisplayPart();
}
/******************************************************************************
function : Enter sleep mode
parameter:
******************************************************************************/
void EPD_2IN13_V2_Sleep(void){
EPD_2IN13_V2_SendCommand(0x22); //POWER OFF
EPD_2IN13_V2_SendData(0xC3);
EPD_2IN13_V2_SendCommand(0x20);
EPD_2IN13_V2_SendCommand(0x10); //enter deep sleep
EPD_2IN13_V2_SendData(0x01);
delay(100);
}

72
src/epd/epd_driver.h Normal file
View File

@ -0,0 +1,72 @@
/*****************************************************************************
* | File : EPD_2in13_V2.h
* | Author : Waveshare team
* | Function : 2.13inch e-paper V2
* | Info :
*----------------
* | This version: V3.0
* | Date : 2019-06-13
* | Info :
* -----------------------------------------------------------------------------
* V3.0(2019-06-13):
* 1.Change name:
* EPD_Reset() => EPD_2IN13_V2_Reset()
* EPD_SendCommand() => EPD_2IN13_V2_SendCommand()
* EPD_SendData() => EPD_2IN13_V2_SendData()
* EPD_WaitUntilIdle() => EPD_2IN13_V2_ReadBusy()
* EPD_Init() => EPD_2IN13_V2_Init()
* EPD_Clear() => EPD_2IN13_V2_Clear()
* EPD_Display() => EPD_2IN13_V2_Display()
* EPD_Sleep() => EPD_2IN13_V2_Sleep()
* 2.add:
* EPD_2IN13_V2_DisplayPartBaseImage()
* -----------------------------------------------------------------------------
* V2.0(2018-11-14):
* 1.Remove:ImageBuff[EPD_HEIGHT * EPD_WIDTH / 8]
* 2.Change:EPD_2IN13_V2_Display(uint8_t *Image)
* Need to pass parameters: pointer to cached data
* 3.Change:
* EPD_RST -> EPD_RST_PIN
* EPD_DC -> EPD_DC_PIN
* EPD_CS -> EPD_CS_PIN
* EPD_BUSY -> EPD_BUSY_PIN
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documnetation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
******************************************************************************/
#ifndef _EPD_2IN13_V2_H_
#define _EPD_2IN13_V2_H_
#include "epd_cfg.h"
// Display resolution
#define EPD_2IN13_V2_WIDTH 122
#define EPD_2IN13_V2_HEIGHT 250
#define EPD_2IN13_V2_FULL 0
#define EPD_2IN13_V2_PART 1
void EPD_2IN13_V2_Init(uint8_t Mode);
void EPD_2IN13_V2_Clear(void);
void EPD_2IN13_V2_Display(uint8_t *Image);
void EPD_2IN13_V2_DisplayPart(uint8_t *Image);
void EPD_2IN13_V2_DisplayPartBaseImage(uint8_t *Image);
void EPD_2IN13_V2_Sleep(void);
#endif

730
src/epd/epd_paint.cpp Normal file
View File

@ -0,0 +1,730 @@
/******************************************************************************
* | File : GUI_Paint.c
* | Author : Waveshare electronics
* | Function : Achieve drawing: draw points, lines, boxes, circles and
* their size, solid dotted line, solid rectangle hollow
* rectangle, solid circle hollow circle.
* | Info :
* Achieve display characters: Display a single character, string, number
* Achieve time display: adaptive size display time minutes and seconds
*----------------
* | This version: V3.2
* | Date : 2020-07-23
* | Info :
* -----------------------------------------------------------------------------
* V3.2(2020-07-23):
* 1. Change: Paint_SetScale(uint8_t scale)
* Add scale 7 for 5.65f e-Parper
* 2. Change: Paint_SetPixel(uint16_t Xpoint, uint16_t Ypoint, uint16_t Color)
* Add the branch for scale 7
* 3. Change: Paint_Clear(uint16_t Color)
* Add the branch for scale 7
*
* V3.1(2019-10-10):
* 1. Add gray level
* PAINT Add Scale
* 2. Add void Paint_SetScale(uint8_t scale);
*
* V3.0(2019-04-18):
* 1.Change:
* Paint_DrawPoint(..., DOT_STYLE DOT_STYLE)
* => Paint_DrawPoint(..., DOT_STYLE Dot_Style)
* Paint_DrawLine(..., LINE_STYLE Line_Style, DOT_PIXEL Dot_Pixel)
* => Paint_DrawLine(..., DOT_PIXEL Line_width, LINE_STYLE Line_Style)
* Paint_DrawRectangle(..., DRAW_FILL Filled, DOT_PIXEL Dot_Pixel)
* => Paint_DrawRectangle(..., DOT_PIXEL Line_width, DRAW_FILL Draw_Fill)
* Paint_DrawCircle(..., DRAW_FILL Draw_Fill, DOT_PIXEL Dot_Pixel)
* => Paint_DrawCircle(..., DOT_PIXEL Line_width, DRAW_FILL Draw_Filll)
*
* -----------------------------------------------------------------------------
* V2.0(2018-11-15):
* 1.add: Paint_NewImage()
* Create an image's properties
* 2.add: Paint_SelectImage()
* Select the picture to be drawn
* 3.add: Paint_SetRotate()
* Set the direction of the cache
* 4.add: Paint_RotateImage()
* Can flip the picture, Support 0-360 degrees,
* but only 90.180.270 rotation is better
* 4.add: Paint_SetMirroring()
* Can Mirroring the picture, horizontal, vertical, origin
* 5.add: Paint_DrawString_CN()
* Can display Chinese(GB1312)
*
* -----------------------------------------------------------------------------
* V1.0(2018-07-17):
* Create library
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documnetation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
******************************************************************************/
#include "epd_paint.h"
#include "epd_cfg.h"
#include "debug.h"
#include <Arduino.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h> //memset()
#include <math.h>
PAINT Paint;
/******************************************************************************
function: Create Image
parameter:
image : Pointer to the image cache
width : The width of the picture
Height : The height of the picture
Color : Whether the picture is inverted
******************************************************************************/
void Paint_NewImage(uint8_t *image, uint16_t Width, uint16_t Height, uint16_t Rotate, uint16_t Color){
Paint.Image = NULL;
Paint.Image = image;
Paint.WidthMemory = Width;
Paint.HeightMemory = Height;
Paint.Color = Color;
Paint.Scale = 2;
Paint.WidthByte = (Width % 8 == 0)? (Width / 8 ): (Width / 8 + 1);
Paint.HeightByte = Height;
Paint.Rotate = Rotate;
Paint.Mirror = MIRROR_NONE;
if(Rotate == ROTATE_0 || Rotate == ROTATE_180) {
Paint.Width = Width;
Paint.Height = Height;
} else {
Paint.Width = Height;
Paint.Height = Width;
}
}
/******************************************************************************
function: Select Image
parameter:
image : Pointer to the image cache
******************************************************************************/
void Paint_SelectImage(uint8_t *image){
Paint.Image = image;
}
/******************************************************************************
function: Select Image Rotate
parameter:
Rotate : 0,90,180,270
******************************************************************************/
void Paint_SetRotate(uint16_t Rotate){
if(Rotate == ROTATE_0 || Rotate == ROTATE_90 || Rotate == ROTATE_180 || Rotate == ROTATE_270) {
// Debug("Set image Rotate %d\r\n", Rotate);
Paint.Rotate = Rotate;
} else {
Debug("rotate = 0, 90, 180, 270\r\n");
}
}
/******************************************************************************
function: Select Image mirror
parameter:
mirror :Not mirror,Horizontal mirror,Vertical mirror,Origin mirror
******************************************************************************/
void Paint_SetMirroring(uint8_t mirror){
if(mirror == MIRROR_NONE || mirror == MIRROR_HORIZONTAL ||
mirror == MIRROR_VERTICAL || mirror == MIRROR_ORIGIN) {
Paint.Mirror = mirror;
} else {
Debug("mirror should be MIRROR_NONE, MIRROR_HORIZONTAL, \
MIRROR_VERTICAL or MIRROR_ORIGIN\r\n");
}
}
void Paint_SetScale(uint8_t scale){
if(scale == 2){
Paint.Scale = scale;
Paint.WidthByte = (Paint.WidthMemory % 8 == 0)? (Paint.WidthMemory / 8 ): (Paint.WidthMemory / 8 + 1);
}else if(scale == 4) {
Paint.Scale = scale;
Paint.WidthByte = (Paint.WidthMemory % 4 == 0)? (Paint.WidthMemory / 4 ): (Paint.WidthMemory / 4 + 1);
}else if(scale == 7) {
Paint.Scale = 7;
Paint.WidthByte = (Paint.WidthMemory % 2 == 0)? (Paint.WidthMemory / 2 ): (Paint.WidthMemory / 2 + 1);
}else {
Debug("Set Scale Input parameter error\r\n");
Debug("Scale Only support: 2 4 7\r\n");
}
}
/******************************************************************************
function: Draw Pixels
parameter:
Xpoint : At point X
Ypoint : At point Y
Color : Painted colors
******************************************************************************/
void Paint_SetPixel(uint16_t Xpoint, uint16_t Ypoint, uint16_t Color){
if(Xpoint > Paint.Width || Ypoint > Paint.Height){
Debug("Exceeding display boundaries\r\n");
return;
}
uint16_t X, Y;
switch(Paint.Rotate) {
case 0:
X = Xpoint;
Y = Ypoint;
break;
case 90:
X = Paint.WidthMemory - Ypoint - 1;
Y = Xpoint;
break;
case 180:
X = Paint.WidthMemory - Xpoint - 1;
Y = Paint.HeightMemory - Ypoint - 1;
break;
case 270:
X = Ypoint;
Y = Paint.HeightMemory - Xpoint - 1;
break;
default:
return;
}
switch(Paint.Mirror) {
case MIRROR_NONE:
break;
case MIRROR_HORIZONTAL:
X = Paint.WidthMemory - X - 1;
break;
case MIRROR_VERTICAL:
Y = Paint.HeightMemory - Y - 1;
break;
case MIRROR_ORIGIN:
X = Paint.WidthMemory - X - 1;
Y = Paint.HeightMemory - Y - 1;
break;
default:
return;
}
if(X > Paint.WidthMemory || Y > Paint.HeightMemory){
Debug("Exceeding display boundaries\r\n");
return;
}
if(Paint.Scale == 2){
uint32_t Addr = X / 8 + Y * Paint.WidthByte;
uint8_t Rdata = Paint.Image[Addr];
if(Color == BLACK)
Paint.Image[Addr] = Rdata & ~(0x80 >> (X % 8));
else
Paint.Image[Addr] = Rdata | (0x80 >> (X % 8));
}else if(Paint.Scale == 4){
uint32_t Addr = X / 4 + Y * Paint.WidthByte;
Color = Color % 4;//Guaranteed color scale is 4 --- 0~3
uint8_t Rdata = Paint.Image[Addr];
Rdata = Rdata & (~(0xC0 >> ((X % 4)*2)));
Paint.Image[Addr] = Rdata | ((Color << 6) >> ((X % 4)*2));
}else if(Paint.Scale == 7){
uint16_t Width = Paint.WidthMemory*3%8 == 0 ? Paint.WidthMemory*3/8 : Paint.WidthMemory*3/8+1;
uint32_t Addr = (Xpoint * 3) / 8 + Ypoint * Width;
uint8_t shift, Rdata, Rdata2;
shift = (Xpoint+Ypoint*Paint.HeightMemory) % 8;
switch(shift) {
case 0 :
Rdata = Paint.Image[Addr] & 0x1f;
Rdata = Rdata | ((Color << 5) & 0xe0);
Paint.Image[Addr] = Rdata;
break;
case 1 :
Rdata = Paint.Image[Addr] & 0xe3;
Rdata = Rdata | ((Color << 2) & 0x1c);
Paint.Image[Addr] = Rdata;
break;
case 2 :
Rdata = Paint.Image[Addr] & 0xfc;
Rdata2 = Paint.Image[Addr + 1] & 0x7f;
Rdata = Rdata | ((Color >> 1) & 0x03);
Rdata2 = Rdata2 | ((Color << 7) & 0x80);
Paint.Image[Addr] = Rdata;
Paint.Image[Addr + 1] = Rdata2;
break;
case 3 :
Rdata = Paint.Image[Addr] & 0x8f;
Rdata = Rdata | ((Color << 4) & 0x70);
Paint.Image[Addr] = Rdata;
break;
case 4 :
Rdata = Paint.Image[Addr] & 0xf1;
Rdata = Rdata | ((Color << 1) & 0x0e);
Paint.Image[Addr] = Rdata;
break;
case 5 :
Rdata = Paint.Image[Addr] & 0xfe;
Rdata2 = Paint.Image[Addr + 1] & 0x3f;
Rdata = Rdata | ((Color >> 2) & 0x01);
Rdata2 = Rdata2 | ((Color << 6) & 0xc0);
Paint.Image[Addr] = Rdata;
Paint.Image[Addr + 1] = Rdata2;
break;
case 6 :
Rdata = Paint.Image[Addr] & 0xc7;
Rdata = Rdata | ((Color << 3) & 0x38);
Paint.Image[Addr] = Rdata;
break;
case 7 :
Rdata = Paint.Image[Addr] & 0xf8;
Rdata = Rdata | (Color & 0x07);
Paint.Image[Addr] = Rdata;
break;
}
}
}
/******************************************************************************
function: Clear the color of the picture
parameter:
Color : Painted colors
******************************************************************************/
void Paint_Clear(uint16_t Color){
if(Paint.Scale == 2 || Paint.Scale == 4) {
for (uint16_t Y = 0; Y < Paint.HeightByte; Y++) {
for (uint16_t X = 0; X < Paint.WidthByte; X++ ) {//8 pixel = 1 byte
uint32_t Addr = X + Y*Paint.WidthByte;
Paint.Image[Addr] = Color;
}
}
}
if(Paint.Scale == 7) {
Color = (uint8_t)Color;
uint16_t Width = (Paint.WidthMemory * 3 % 8 == 0)? (Paint.WidthMemory * 3 / 8 ): (Paint.WidthMemory * 3 / 8 + 1);
for (uint16_t Y = 0; Y < Paint.HeightByte; Y++) {
for (uint16_t X = 0; X < Width; X++ ) {
uint32_t Addr = X + Y * Width;
if((X + Y * Width)%3 == 0)
Paint.Image[Addr] = ((Color<<5) | (Color<<2) | (Color>>1));
else if((X + Y * Width)%3 == 1)
Paint.Image[Addr] = ((Color<<7) | (Color<<4) | (Color<<1) | (Color>>2));
else if((X + Y * Width)%3 == 2)
Paint.Image[Addr] = ((Color<<6) | (Color<<3) | Color);
}
}
}
}
/******************************************************************************
function: Clear the color of a window
parameter:
Xstart : x starting point
Ystart : Y starting point
Xend : x end point
Yend : y end point
Color : Painted colors
******************************************************************************/
void Paint_ClearWindows(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend, uint16_t Color){
uint16_t X, Y;
for (Y = Ystart; Y < Yend; Y++) {
for (X = Xstart; X < Xend; X++) {//8 pixel = 1 byte
Paint_SetPixel(X, Y, Color);
}
}
}
/******************************************************************************
function: Draw Point(Xpoint, Ypoint) Fill the color
parameter:
Xpoint : The Xpoint coordinate of the point
Ypoint : The Ypoint coordinate of the point
Color : Painted color
Dot_Pixel : point size
Dot_Style : point Style
******************************************************************************/
void Paint_DrawPoint(uint16_t Xpoint, uint16_t Ypoint, uint16_t Color,
DOT_PIXEL Dot_Pixel, DOT_STYLE Dot_Style){
if (Xpoint > Paint.Width || Ypoint > Paint.Height) {
Debug("Paint_DrawPoint Input exceeds the normal display range\r\n");
return;
}
int16_t XDir_Num , YDir_Num;
if (Dot_Style == DOT_FILL_AROUND) {
for (XDir_Num = 0; XDir_Num < 2 * Dot_Pixel - 1; XDir_Num++) {
for (YDir_Num = 0; YDir_Num < 2 * Dot_Pixel - 1; YDir_Num++) {
if(Xpoint + XDir_Num - Dot_Pixel < 0 || Ypoint + YDir_Num - Dot_Pixel < 0)
break;
Paint_SetPixel(Xpoint + XDir_Num - Dot_Pixel, Ypoint + YDir_Num - Dot_Pixel, Color);
}
}
} else {
for (XDir_Num = 0; XDir_Num < Dot_Pixel; XDir_Num++) {
for (YDir_Num = 0; YDir_Num < Dot_Pixel; YDir_Num++) {
Paint_SetPixel(Xpoint + XDir_Num - 1, Ypoint + YDir_Num - 1, Color);
}
}
}
}
/******************************************************************************
function: Draw a line of arbitrary slope
parameter:
Xstart Starting Xpoint point coordinates
Ystart Starting Xpoint point coordinates
Xend End point Xpoint coordinate
Yend End point Ypoint coordinate
Color The color of the line segment
Line_width : Line width
Line_Style: Solid and dotted lines
******************************************************************************/
void Paint_DrawLine(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend,
uint16_t Color, DOT_PIXEL Line_width, LINE_STYLE Line_Style)
{
if (Xstart > Paint.Width || Ystart > Paint.Height ||
Xend > Paint.Width || Yend > Paint.Height) {
Debug("Paint_DrawLine Input exceeds the normal display range\r\n");
return;
}
uint16_t Xpoint = Xstart;
uint16_t Ypoint = Ystart;
int dx = (int)Xend - (int)Xstart >= 0 ? Xend - Xstart : Xstart - Xend;
int dy = (int)Yend - (int)Ystart <= 0 ? Yend - Ystart : Ystart - Yend;
int XAddway = Xstart < Xend ? 1 : -1;
int YAddway = Ystart < Yend ? 1 : -1;
int Esp = dx + dy;
char Dotted_Len = 0;
for (;;) {
Dotted_Len++;
if (Line_Style == LINE_STYLE_DOTTED && Dotted_Len % 3 == 0) {
Paint_DrawPoint(Xpoint, Ypoint, IMAGE_BACKGROUND, Line_width, DOT_STYLE_DFT);
Dotted_Len = 0;
} else {
Paint_DrawPoint(Xpoint, Ypoint, Color, Line_width, DOT_STYLE_DFT);
}
if (2 * Esp >= dy) {
if (Xpoint == Xend)
break;
Esp += dy;
Xpoint += XAddway;
}
if (2 * Esp <= dx) {
if (Ypoint == Yend)
break;
Esp += dx;
Ypoint += YAddway;
}
}
}
/******************************************************************************
function: Draw a rectangle
parameter:
Xstart Rectangular Starting Xpoint point coordinates
Ystart Rectangular Starting Xpoint point coordinates
Xend Rectangular End point Xpoint coordinate
Yend Rectangular End point Ypoint coordinate
Color The color of the Rectangular segment
Line_width: Line width
Draw_Fill : Whether to fill the inside of the rectangle
******************************************************************************/
void Paint_DrawRectangle(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend,
uint16_t Color, DOT_PIXEL Line_width, DRAW_FILL Draw_Fill){
if (Xstart > Paint.Width || Ystart > Paint.Height ||
Xend > Paint.Width || Yend > Paint.Height) {
Debug("Input exceeds the normal display range\r\n");
return;
}
if (Draw_Fill) {
uint16_t Ypoint;
for(Ypoint = Ystart; Ypoint < Yend; Ypoint++) {
Paint_DrawLine(Xstart, Ypoint, Xend, Ypoint, Color , Line_width, LINE_STYLE_SOLID);
}
} else {
Paint_DrawLine(Xstart, Ystart, Xend, Ystart, Color, Line_width, LINE_STYLE_SOLID);
Paint_DrawLine(Xstart, Ystart, Xstart, Yend, Color, Line_width, LINE_STYLE_SOLID);
Paint_DrawLine(Xend, Yend, Xend, Ystart, Color, Line_width, LINE_STYLE_SOLID);
Paint_DrawLine(Xend, Yend, Xstart, Yend, Color, Line_width, LINE_STYLE_SOLID);
}
}
/******************************************************************************
function: Use the 8-point method to draw a circle of the
specified size at the specified position->
parameter:
X_Center Center X coordinate
Y_Center Center Y coordinate
Radius circle Radius
Color The color of the circle segment
Line_width: Line width
Draw_Fill : Whether to fill the inside of the Circle
******************************************************************************/
void Paint_DrawCircle(uint16_t X_Center, uint16_t Y_Center, uint16_t Radius,
uint16_t Color, DOT_PIXEL Line_width, DRAW_FILL Draw_Fill){
if (X_Center > Paint.Width || Y_Center >= Paint.Height) {
Debug("Paint_DrawCircle Input exceeds the normal display range\r\n");
return;
}
int16_t XCurrent, YCurrent;
XCurrent = 0;
YCurrent = Radius;
int16_t Esp = 3 - (Radius << 1 );
int16_t sCountY;
if (Draw_Fill == DRAW_FILL_FULL) {
while (XCurrent <= YCurrent ) {
for (sCountY = XCurrent; sCountY <= YCurrent; sCountY ++ ) {
Paint_DrawPoint(X_Center + XCurrent, Y_Center + sCountY, Color, DOT_PIXEL_DFT, DOT_STYLE_DFT);//1
Paint_DrawPoint(X_Center - XCurrent, Y_Center + sCountY, Color, DOT_PIXEL_DFT, DOT_STYLE_DFT);//2
Paint_DrawPoint(X_Center - sCountY, Y_Center + XCurrent, Color, DOT_PIXEL_DFT, DOT_STYLE_DFT);//3
Paint_DrawPoint(X_Center - sCountY, Y_Center - XCurrent, Color, DOT_PIXEL_DFT, DOT_STYLE_DFT);//4
Paint_DrawPoint(X_Center - XCurrent, Y_Center - sCountY, Color, DOT_PIXEL_DFT, DOT_STYLE_DFT);//5
Paint_DrawPoint(X_Center + XCurrent, Y_Center - sCountY, Color, DOT_PIXEL_DFT, DOT_STYLE_DFT);//6
Paint_DrawPoint(X_Center + sCountY, Y_Center - XCurrent, Color, DOT_PIXEL_DFT, DOT_STYLE_DFT);//7
Paint_DrawPoint(X_Center + sCountY, Y_Center + XCurrent, Color, DOT_PIXEL_DFT, DOT_STYLE_DFT);
}
if (Esp < 0 )
Esp += 4 * XCurrent + 6;
else {
Esp += 10 + 4 * (XCurrent - YCurrent );
YCurrent --;
}
XCurrent ++;
}
} else {
while (XCurrent <= YCurrent ) {
Paint_DrawPoint(X_Center + XCurrent, Y_Center + YCurrent, Color, Line_width, DOT_STYLE_DFT);//1
Paint_DrawPoint(X_Center - XCurrent, Y_Center + YCurrent, Color, Line_width, DOT_STYLE_DFT);//2
Paint_DrawPoint(X_Center - YCurrent, Y_Center + XCurrent, Color, Line_width, DOT_STYLE_DFT);//3
Paint_DrawPoint(X_Center - YCurrent, Y_Center - XCurrent, Color, Line_width, DOT_STYLE_DFT);//4
Paint_DrawPoint(X_Center - XCurrent, Y_Center - YCurrent, Color, Line_width, DOT_STYLE_DFT);//5
Paint_DrawPoint(X_Center + XCurrent, Y_Center - YCurrent, Color, Line_width, DOT_STYLE_DFT);//6
Paint_DrawPoint(X_Center + YCurrent, Y_Center - XCurrent, Color, Line_width, DOT_STYLE_DFT);//7
Paint_DrawPoint(X_Center + YCurrent, Y_Center + XCurrent, Color, Line_width, DOT_STYLE_DFT);//0
if (Esp < 0 )
Esp += 4 * XCurrent + 6;
else {
Esp += 10 + 4 * (XCurrent - YCurrent );
YCurrent --;
}
XCurrent ++;
}
}
}
/******************************************************************************
function: Show English characters
parameter:
Xpoint X coordinate
Ypoint Y coordinate
Acsii_Char To display the English characters
Font A structure pointer that displays a character size
Color_Foreground : Select the foreground color
Color_Background : Select the background color
******************************************************************************/
void Paint_DrawChar(uint16_t Xpoint, uint16_t Ypoint, const char Acsii_Char,
sFONT* Font, uint16_t Color_Foreground, uint16_t Color_Background){
uint16_t Page, Column;
if (Xpoint > Paint.Width || Ypoint > Paint.Height) {
Debug("Paint_DrawChar Input exceeds the normal display range\r\n");
return;
}
uint32_t Char_Offset = (Acsii_Char - ' ') * Font->Height * (Font->Width / 8 + (Font->Width % 8 ? 1 : 0));
const unsigned char *ptr = &Font->table[Char_Offset];
for (Page = 0; Page < Font->Height; Page ++ ) {
for (Column = 0; Column < Font->Width; Column ++ ) {
if (FONT_BACKGROUND == Color_Background) {
if (*ptr & (0x80 >> (Column % 8)))
Paint_SetPixel(Xpoint + Column, Ypoint + Page, Color_Foreground);
} else {
if (*ptr & (0x80 >> (Column % 8))) {
Paint_SetPixel(Xpoint + Column, Ypoint + Page, Color_Foreground);
} else {
Paint_SetPixel(Xpoint + Column, Ypoint + Page, Color_Background);
}
}
if (Column % 8 == 7) ptr++;
}
if (Font->Width % 8 != 0)
ptr++;
}
}
void Paint_DrawIcon(uint16_t Xpoint, uint16_t Ypoint, const ICON_tpe icon,
sFONT* Font, uint16_t Color_Foreground, uint16_t Color_Background){
uint16_t Page, Column;
if (Xpoint > Paint.Width || Ypoint > Paint.Height) {
Debug("Paint_DrawIcon Input exceeds the normal display range\r\n");
return;
}
uint32_t Char_Offset = (icon) * Font->Height * (Font->Width / 8 + (Font->Width % 8 ? 1 : 0));
const unsigned char *ptr = &Font->table[Char_Offset];
for (Page = 0; Page < Font->Height; Page ++ ) {
for (Column = 0; Column < Font->Width; Column ++ ) {
if (FONT_BACKGROUND == Color_Background) {
if (*ptr & (0x80 >> (Column % 8)))
Paint_SetPixel(Xpoint + Column, Ypoint + Page, Color_Foreground);
} else {
if (*ptr & (0x80 >> (Column % 8))) {
Paint_SetPixel(Xpoint + Column, Ypoint + Page, Color_Foreground);
} else {
Paint_SetPixel(Xpoint + Column, Ypoint + Page, Color_Background);
}
}
if (Column % 8 == 7) ptr++;
}
if (Font->Width % 8 != 0)
ptr++;
}
}
/******************************************************************************
function: Display the string
parameter:
Xstart X coordinate
Ystart Y coordinate
pString The first address of the English string to be displayed
Font A structure pointer that displays a character size
Color_Foreground : Select the foreground color
Color_Background : Select the background color
******************************************************************************/
void Paint_DrawString_EN(uint16_t Xstart, uint16_t Ystart, const char * pString,
sFONT* Font, uint16_t Color_Foreground, uint16_t Color_Background){
uint16_t Xpoint = Xstart;
uint16_t Ypoint = Ystart;
if (Xstart > Paint.Width || Ystart > Paint.Height) {
Debug("Paint_DrawString_EN Input exceeds the normal display range\r\n");
return;
}
while (* pString != '\0') {
if ((Xpoint + Font->Width ) > Paint.Width ) {
Xpoint = Xstart;
Ypoint += Font->Height;
}
if ((Ypoint + Font->Height ) > Paint.Height ) {
Xpoint = Xstart;
Ypoint = Ystart;
}
Paint_DrawChar(Xpoint, Ypoint, * pString, Font, Color_Background, Color_Foreground);
pString ++;
Xpoint += Font->Width;
}
}
/******************************************************************************
function: Display nummber
parameter:
Xstart X coordinate
Ystart : Y coordinate
Nummber : The number displayed
Font A structure pointer that displays a character size
Color_Foreground : Select the foreground color
Color_Background : Select the background color
******************************************************************************/
#define ARRAY_LEN 255
void Paint_DrawIntUnit(uint16_t Xpoint, uint16_t Ypoint, int32_t Number,const char * pString, sFONT* Font, uint16_t Color_Foreground, uint16_t Color_Background){
int16_t Str_Bit = 0, Unit_Bit = 0;
uint8_t Str_Array[ARRAY_LEN] = {0};
uint8_t *pStr = Str_Array;
if (Xpoint > Paint.Width || Ypoint > Paint.Height) {
Debug("Paint_DisNum Input exceeds the normal display range\r\n");
return;
}
itoa(Number, (char*)Str_Array, 10);
while (Str_Array[Str_Bit] != '\0') {
++Str_Bit;
}
while (pString[Unit_Bit] != '\0') {
Str_Array[Str_Bit] = pString[Unit_Bit];
++Unit_Bit;
++Str_Bit;
}
Paint_DrawString_EN(Xpoint, Ypoint, (const char*)pStr, Font, Color_Background, Color_Foreground);
}
void Paint_DrawFltUnit(uint16_t Xpoint, uint16_t Ypoint, float Number,const char * pString, sFONT* Font, uint16_t Color_Foreground, uint16_t Color_Background){
int16_t Str_Bit = 0, Unit_Bit = 0;
uint8_t Str_Array[ARRAY_LEN] = {0};
uint8_t *pStr = Str_Array;
if (Xpoint > Paint.Width || Ypoint > Paint.Height) {
Debug("Paint_DisNum Input exceeds the normal display range\r\n");
return;
}
dtostrf(Number, -4, 2, (char*)Str_Array);
while (Str_Array[Str_Bit] != '\0') {
++Str_Bit;
}
while (pString[Unit_Bit] != '\0') {
Str_Array[Str_Bit] = pString[Unit_Bit];
++Unit_Bit;
++Str_Bit;
}
Paint_DrawString_EN(Xpoint, Ypoint, (const char*)pStr, Font, Color_Background, Color_Foreground);
}
/******************************************************************************
function: Display time
parameter:
Xstart X coordinate
Ystart : Y coordinate
pTime : Time-related structures
Font A structure pointer that displays a character size
Color_Foreground : Select the foreground color
Color_Background : Select the background color
******************************************************************************/
void Paint_DrawTime(uint16_t Xstart, uint16_t Ystart, strDateTime *pTime, sFONT* Font,
uint16_t Color_Foreground, uint16_t Color_Background){
uint8_t value[10] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
uint16_t Dx = Font->Width;
//Write data into the cache
Paint_DrawChar(Xstart , Ystart, value[pTime->hour / 10], Font, Color_Background, Color_Foreground);
Paint_DrawChar(Xstart + Dx , Ystart, value[pTime->hour % 10], Font, Color_Background, Color_Foreground);
Paint_DrawChar(Xstart + Dx + Dx / 4 + Dx / 2 , Ystart, ':' , Font, Color_Background, Color_Foreground);
Paint_DrawChar(Xstart + Dx * 2 + Dx / 2 , Ystart, value[pTime->minute / 10] , Font, Color_Background, Color_Foreground);
Paint_DrawChar(Xstart + Dx * 3 + Dx / 2 , Ystart, value[pTime->minute % 10] , Font, Color_Background, Color_Foreground);
}

127
src/epd/epd_paint.h Normal file
View File

@ -0,0 +1,127 @@
#ifndef __GUI_PAINT_H
#define __GUI_PAINT_H
#include "debug.h"
#include "epd_cfg.h"
#include "fonts/fonts.h"
#include "../time/time.h"
/**
* Image attributes
**/
typedef struct {
uint8_t *Image;
uint16_t Width;
uint16_t Height;
uint16_t WidthMemory;
uint16_t HeightMemory;
uint16_t Color;
uint16_t Rotate;
uint16_t Mirror;
uint16_t WidthByte;
uint16_t HeightByte;
uint16_t Scale;
} PAINT;
extern PAINT Paint;
/**
* Display rotate
**/
#define ROTATE_0 0
#define ROTATE_90 90
#define ROTATE_180 180
#define ROTATE_270 270
/**
* Display Flip
**/
typedef enum {
MIRROR_NONE = 0x00,
MIRROR_HORIZONTAL = 0x01,
MIRROR_VERTICAL = 0x02,
MIRROR_ORIGIN = 0x03,
} MIRROR_IMAGE;
#define MIRROR_IMAGE_DFT MIRROR_NONE
/**
* image color
**/
#define WHITE 0xFF
#define BLACK 0x00
#define RED BLACK
#define IMAGE_BACKGROUND WHITE
#define FONT_FOREGROUND BLACK
#define FONT_BACKGROUND WHITE
//4 Gray level
#define GRAY1 0x03 //Blackest
#define GRAY2 0x02
#define GRAY3 0x01 //gray
#define GRAY4 0x00 //white
/**
* The size of the point
**/
typedef enum {
DOT_PIXEL_1X1 = 1, // 1 x 1
DOT_PIXEL_2X2 , // 2 X 2
DOT_PIXEL_3X3 , // 3 X 3
DOT_PIXEL_4X4 , // 4 X 4
DOT_PIXEL_5X5 , // 5 X 5
DOT_PIXEL_6X6 , // 6 X 6
DOT_PIXEL_7X7 , // 7 X 7
DOT_PIXEL_8X8 , // 8 X 8
} DOT_PIXEL;
#define DOT_PIXEL_DFT DOT_PIXEL_1X1 //Default dot pilex
/**
* Point size fill style
**/
typedef enum {
DOT_FILL_AROUND = 1, // dot pixel 1 x 1
DOT_FILL_RIGHTUP , // dot pixel 2 X 2
} DOT_STYLE;
#define DOT_STYLE_DFT DOT_FILL_AROUND //Default dot pilex
/**
* Line style, solid or dashed
**/
typedef enum {
LINE_STYLE_SOLID = 0,
LINE_STYLE_DOTTED,
} LINE_STYLE;
/**
* Whether the graphic is filled
**/
typedef enum {
DRAW_FILL_EMPTY = 0,
DRAW_FILL_FULL,
} DRAW_FILL;
//init and Clear
void Paint_NewImage(uint8_t *image, uint16_t Width, uint16_t Height, uint16_t Rotate, uint16_t Color);
void Paint_SelectImage(uint8_t *image);
void Paint_SetRotate(uint16_t Rotate);
void Paint_SetMirroring(uint8_t mirror);
void Paint_SetPixel(uint16_t Xpoint, uint16_t Ypoint, uint16_t Color);
void Paint_SetScale(uint8_t scale);
void Paint_Clear(uint16_t Color);
void Paint_ClearWindows(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend, uint16_t Color);
//Drawing
// void Paint_DrawPoint(uint16_t Xpoint, uint16_t Ypoint, uint16_t Color, DOT_PIXEL Dot_Pixel, DOT_STYLE Dot_FillWay);
// void Paint_DrawLine(uint16_t Xstart, uint16_t Ystart, uint16_t Xend, uint16_t Yend, uint16_t Color, DOT_PIXEL Line_width, LINE_STYLE Line_Style);
//Display string
void Paint_DrawChar(uint16_t Xstart, uint16_t Ystart, const char Acsii_Char, sFONT* Font, uint16_t Color_Foreground, uint16_t Color_Background);
void Paint_DrawIcon(uint16_t Xpoint, uint16_t Ypoint, const ICON_tpe icon, sFONT* Font, uint16_t Color_Foreground, uint16_t Color_Background);
void Paint_DrawString_EN(uint16_t Xstart, uint16_t Ystart, const char * pString, sFONT* Font, uint16_t Color_Foreground, uint16_t Color_Background);
void Paint_DrawIntUnit(uint16_t Xpoint, uint16_t Ypoint, int32_t Number,const char * pString, sFONT* Font, uint16_t Color_Foreground, uint16_t Color_Background);
void Paint_DrawFltUnit(uint16_t Xpoint, uint16_t Ypoint, float Number,const char * pString, sFONT* Font, uint16_t Color_Foreground, uint16_t Color_Background);
void Paint_DrawTime(uint16_t Xstart, uint16_t Ystart, strDateTime *pTime, sFONT* Font, uint16_t Color_Foreground, uint16_t Color_Background);
#endif

1384
src/epd/fonts/font12.cpp Normal file

File diff suppressed because it is too large Load Diff

1764
src/epd/fonts/font16.cpp Normal file

File diff suppressed because it is too large Load Diff

2142
src/epd/fonts/font20.cpp Normal file

File diff suppressed because it is too large Load Diff

2520
src/epd/fonts/font24.cpp Normal file

File diff suppressed because it is too large Load Diff

2208
src/epd/fonts/font45.cpp Normal file

File diff suppressed because it is too large Load Diff

1004
src/epd/fonts/font8.cpp Normal file

File diff suppressed because it is too large Load Diff

90
src/epd/fonts/fonts.h Normal file
View File

@ -0,0 +1,90 @@
/**
******************************************************************************
* @file fonts.h
* @author MCD Application Team
* @version V1.0.0
* @date 18-February-2014
* @brief Header for fonts.c file
******************************************************************************
* @attention
*
* <h2><center>&copy; COPYRIGHT(c) 2014 STMicroelectronics</center></h2>
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __FONTS_H
#define __FONTS_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include <stdint.h>
//ASCII
typedef struct _tFont
{
const uint8_t *table;
uint16_t Width;
uint16_t Height;
} sFONT;
extern sFONT Font45;
extern sFONT Font24;
extern sFONT Font20;
extern sFONT Font16;
extern sFONT Font12;
extern sFONT Font8;
extern sFONT FontIcon;
typedef enum {
ICON_LOGO = 0,
ICON_WARN,
ICON_TEMPERATURE, ICON_DROP, ICON_SCALE, ICON_POLUTION,
ICON_SUN,
ICON_CLOUD, ICON_CLOUD_RAIN, ICON_CLOUD_SNOW,
ICON_CLOUD_THUNDER, ICON_CLOUD_SUN,
ICON_SUNSET, ICON_SUNRISE,
} ICON_tpe;
#ifdef __cplusplus
}
#endif
#endif /* __FONTS_H */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

1124
src/epd/fonts/icons.cpp Normal file

File diff suppressed because it is too large Load Diff

633
src/epd/icon.h Normal file
View File

@ -0,0 +1,633 @@
/* GIMP header image file format (INDEXED): /home/sora/files/sora/metesp/metesp/src/epd/icon.h */
static unsigned int width = 72;
static unsigned int height = 72;
/* Call this macro repeatedly. After each use, the pixel data can be extracted */
#define HEADER_PIXEL(data,pixel) {\
pixel[0] = header_data_cmap[(unsigned char)data[0]][0]; \
pixel[1] = header_data_cmap[(unsigned char)data[0]][1]; \
pixel[2] = header_data_cmap[(unsigned char)data[0]][2]; \
data ++; }
static unsigned char header_data_cmap[256][3] = {
{ 0, 0, 0},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255},
{255,255,255}
};
static unsigned char header_data[] = {
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,
1,0,0,1,1,0,0,1,1,1,1,1,1,1,1,1,
1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,
1,0,0,1,1,0,0,1,1,1,1,1,1,1,1,1,
1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,
0,0,1,1,1,1,0,0,1,1,1,1,1,1,1,0,
0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,0,0,1,0,0,0,0,1,1,1,1,1,
0,0,1,1,1,1,0,0,1,1,1,1,1,1,0,0,
0,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,0,0,1,1,0,0,0,0,1,1,1,0,
0,1,1,1,1,1,1,0,0,1,1,1,0,0,0,0,
1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,0,0,1,1,1,1,0,0,0,1,1,0,
0,1,1,1,1,1,1,0,0,1,1,0,0,0,1,1,
1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,0,0,1,1,1,1,1,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,
1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,0,0,1,1,1,1,1,1,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,
1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,0,0,1,1,1,0,0,0,0,0,0,
1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,
1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,0,0,1,1,0,0,0,0,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,
1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,
0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,
0,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,
1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,0,0,0,0,0,0,1,1,1,1,1,0,0,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,
1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,0,0,0,1,1,1,1,1,1,0,0,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,
1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,0,0,1,1,1,1,1,0,0,0,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,
1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,0,0,1,1,1,1,0,0,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,
1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,0,0,0,1,1,0,0,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,
1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,0,0,1,0,0,0,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,
0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,
0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,0,
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,0,0,1,1,0,0,0,0,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,0,
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,0,0,1,1,1,1,0,0,0,
0,1,1,1,1,1,1,1,
1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,0,
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,
0,0,0,1,1,1,1,1,
1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,0,
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,
0,0,0,1,1,1,1,1,
1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,0,
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,0,0,1,1,1,1,0,0,0,
0,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,0,
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,0,0,1,1,0,0,0,0,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,
0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,
0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,
0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,
1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,0,0,1,0,0,0,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,
1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,0,0,1,1,1,0,0,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,
1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,0,0,1,1,1,1,0,0,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,
1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,0,0,1,1,1,1,1,0,0,0,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,
1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,0,0,0,1,1,1,1,1,1,0,0,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,
1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,0,0,0,0,0,0,1,1,1,1,1,0,0,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,
0,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,
0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,
0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,0,0,1,1,0,0,0,0,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,
1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,0,0,1,1,1,1,0,0,0,0,0,
0,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,
1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,0,0,1,1,1,1,1,1,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,
1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,0,0,1,1,1,1,1,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,
1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,0,0,1,1,1,1,0,0,0,1,1,0,
0,1,1,1,1,1,1,0,0,1,1,0,0,0,1,1,
1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,0,0,1,1,0,0,0,0,1,1,1,0,
0,1,1,1,1,1,1,0,0,1,1,1,0,0,0,0,
1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,0,0,1,0,0,0,1,1,1,1,1,1,
0,0,1,1,1,1,0,0,1,1,1,1,1,1,0,0,
0,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,
0,0,1,1,1,1,0,0,1,1,1,1,1,1,1,0,
0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,
1,0,0,1,1,0,0,1,1,1,1,1,1,1,1,1,
1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,
1,0,0,1,1,0,0,1,1,1,1,1,1,1,1,1,
1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1
};

166
src/influxdb/influx.h Normal file
View File

@ -0,0 +1,166 @@
#ifndef TG_INFLUX_H
#define TG_INFLUX_H
#define DEVICE "ESP32"
#include <Arduino.h>
#include <InfluxDbClient.h>
#include "../sensor/sensor.h"
#include "../owm/owm.h"
const char* cert = "-----BEGIN CERTIFICATE-----\n\
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw\n\
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n\
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4\n\
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu\n\
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY\n\
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc\n\
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+\n\
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U\n\
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW\n\
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH\n\
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC\n\
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv\n\
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn\n\
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn\n\
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw\n\
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI\n\
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\n\
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq\n\
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL\n\
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ\n\
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK\n\
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5\n\
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur\n\
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC\n\
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc\n\
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq\n\
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA\n\
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d\n\
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=\n\
-----END CERTIFICATE-----\n\
";
class Influx {
public:
Influx(Sensor* _sensor, OWM* _owm0, OWM* _owm1): sensor(_sensor), owm0(_owm0), owm1(_owm1) {}
void check(){
if (client.validateConnection()) {
Serial.print("Connected to InfluxDB: ");
Serial.println(client.getServerUrl());
} else {
Serial.print("InfluxDB connection failed: ");
Serial.println(client.getLastErrorMessage());
}
}
void record(){
if(owm0) record_weather(owm0);
if(owm1) record_weather(owm1);
if(sensor) record_local();
}
void record(bool w, bool s){
if(owm0 && w) record_weather(owm0);
if(owm1 && w) record_weather(owm1);
if(sensor && s) record_local();
}
void record_local(){
if(!sensor) return;
dp.clearFields();
dp.clearTags();
dp.addTag("device", "WESP0");
dp.addTag("location", "local");
dp.addField("temperature", sensor->temp);
dp.addField("pressure", sensor->pres);
dp.addField("humidity", sensor->temp);
dp.addField("heat_index", sensor->heatidx);
dp.addField("light", sensor->light);
dp.addField("uv", sensor->uv);
dp.addField("uvi", sensor->uvi);
dp.addField("light_full", sensor->light_full);
dp.addField("light_vis", sensor->light_vis);
dp.addField("light_ir", sensor->light_ir);
dp.addField("light_lux", sensor->light_lux);
dp.addField("voc", sensor->voci);
dp.addField("pm10_standard", sensor->pmd.pm10_standard);
dp.addField("pm25_standard", sensor->pmd.pm25_standard);
dp.addField("pm100_standard", sensor->pmd.pm100_standard);
dp.addField("pm10_env", sensor->pmd.pm10_env);
dp.addField("pm25_env", sensor->pmd.pm25_env);
dp.addField("pm100_env", sensor->pmd.pm100_env);
dp.addField("particles_03um", sensor->pmd.particles_03um);
dp.addField("particles_05um", sensor->pmd.particles_05um);
dp.addField("particles_10um", sensor->pmd.particles_10um);
dp.addField("particles_25um", sensor->pmd.particles_25um);
dp.addField("particles_50um", sensor->pmd.particles_50um);
dp.addField("particles_100um", sensor->pmd.particles_100um);
client.writePoint(dp);
}
void record_weather(OWM* owm){
if(!owm) return;
if(!owm->valid_weather) return;
JsonObject weather_0 = owm->weather["weather"][0];
JsonObject weather_main = owm->weather["main"];
JsonObject weather_sys = owm->weather["sys"];
if(!weather_0 || !weather_main || !weather_sys ) return;
dp.clearFields();
dp.clearTags();
dp.addTag("device", "WESP0");
dp.addTag("location", owm->location);
dp.addField("temperature", float(weather_main["temp"]));
dp.addField("temperature_feel",float(weather_main["feels_like"]));
dp.addField("pressure",int32_t(weather_main["pressure"]));
dp.addField("humidity",int32_t(weather_main["humidity"]));
JsonObject weather_wind = owm->weather["wind"];
dp.addField("wind_speed",weather_wind?float(weather_wind["speed"]):0.0);
dp.addField("wind_dir",weather_wind?float(weather_wind["deg"]):0.0);
dp.addField("wind_gust",weather_wind?float(weather_wind["gust"]):0.0);
dp.addField("sunrise", uint64_t(weather_sys["sunrise"]));
dp.addField("sunset",uint64_t(weather_sys["sunset"]));
dp.addField("suntime",uint64_t(weather_sys["sunset"])-uint64_t(weather_sys["sunrise"]));
JsonObject weather_rain = owm->weather["rain"];
dp.addField("precipitation_rain_1h",weather_rain?float(weather_rain["1h"]):0.0);
dp.addField("precipitation_rain_3h",weather_rain?float(weather_rain["3h"]):0.0);
JsonObject weather_snow = owm->weather["snow"];
dp.addField("precipitation_snow_1h",weather_snow?float(weather_snow["1h"]):0.0);
dp.addField("precipitation_snow_3h",weather_snow?float(weather_snow["3h"]):0.0);
client.writePoint(dp);
}
private:
OWM* owm0;
OWM* owm1;
Sensor* sensor;
InfluxDBClient client = InfluxDBClient(INFLUXDB_URL, INFLUXDB_ORG, INFLUXDB_BUCKET, INFLUXDB_TOKEN, cert);
Point dp = Point("environment");
};
#endif

225
src/ntpt/ntp.cpp Normal file
View File

@ -0,0 +1,225 @@
#include "ntp.h"
#define LEAP_YEAR(Y) ( ((1970+Y)>0) && !((1970+Y)%4) && ( ((1970+Y)%100) || !((1970+Y)%400) ) )
#define SEC_TO_MS 1000
#define RECV_TIMEOUT_DEFATUL 1 // 1 second
#define SEND_INTRVL_DEFAULT 1 // 1 second
#define MAX_SEND_INTERVAL 60 // 60 seconds
#define MAC_RECV_TIMEOUT 60 // 60 seconds
const int NTP_PACKET_SIZE = 48;
byte _packetBuffer[ NTP_PACKET_SIZE];
static const uint8_t _monthDays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
float _timeZone=0.0;
String _NTPserver="";
// NTPserver is the name of the NTPserver
bool NTPtime::setSendInterval(unsigned long _sendInterval_) {
bool retVal = false;
if(_sendInterval_ <= MAX_SEND_INTERVAL) {
_sendInterval = _sendInterval_ * SEC_TO_MS;
retVal = true;
}
return retVal;
}
bool NTPtime::setRecvTimeout(unsigned long _recvTimeout_) {
bool retVal = false;
if(_recvTimeout_ <= MAC_RECV_TIMEOUT) {
_recvTimeout = _recvTimeout_ * SEC_TO_MS;
retVal = true;
}
return retVal;
}
NTPtime::NTPtime(String NTPserver) {
_NTPserver = NTPserver;
_sendPhase = true;
_sentTime = 0;
_sendInterval = SEND_INTRVL_DEFAULT * SEC_TO_MS;
_recvTimeout = RECV_TIMEOUT_DEFATUL * SEC_TO_MS;
}
// Converts a unix time stamp to a strDateTime structure
strDateTime NTPtime::ConvertUnixTimestamp( unsigned long _tempTimeStamp) {
strDateTime _tempDateTime;
uint8_t _year, _month, _monthLength;
uint32_t _time;
unsigned long _days;
_time = (uint32_t)_tempTimeStamp;
_tempDateTime.second = _time % 60;
_time /= 60; // now it is minutes
_tempDateTime.minute = _time % 60;
_time /= 60; // now it is hours
_tempDateTime.hour = _time % 24;
_time /= 24; // now it is _days
_tempDateTime.dayofWeek = ((_time + 4) % 7) + 1; // Sunday is day 1
_year = 0;
_days = 0;
while ((unsigned)(_days += (LEAP_YEAR(_year) ? 366 : 365)) <= _time) {
_year++;
}
_tempDateTime.year = _year; // year is offset from 1970
_days -= LEAP_YEAR(_year) ? 366 : 365;
_time -= _days; // now it is days in this year, starting at 0
_days = 0;
_month = 0;
_monthLength = 0;
for (_month = 0; _month < 12; _month++) {
if (_month == 1) { // february
if (LEAP_YEAR(_year)) {
_monthLength = 29;
} else {
_monthLength = 28;
}
} else {
_monthLength = _monthDays[_month];
}
if (_time >= _monthLength) {
_time -= _monthLength;
} else {
break;
}
}
_tempDateTime.month = _month + 1; // jan is month 1
_tempDateTime.day = _time + 1; // day of month
_tempDateTime.year += 1970;
return _tempDateTime;
}
//
// Summertime calculates the daylight saving time for middle Europe. Input: Unixtime in UTC
//
boolean NTPtime::summerTime(unsigned long _timeStamp ) {
strDateTime _tempDateTime;
_tempDateTime = ConvertUnixTimestamp(_timeStamp);
if (_tempDateTime.month < 3 || _tempDateTime.month > 10) return false; // keine Sommerzeit in Jan, Feb, Nov, Dez
if (_tempDateTime.month > 3 && _tempDateTime.month < 10) return true; // Sommerzeit in Apr, Mai, Jun, Jul, Aug, Sep
if (_tempDateTime.month == 3 && (_tempDateTime.hour + 24 * _tempDateTime.day) >= (3 + 24 * (31 - (5 * _tempDateTime.year / 4 + 4) % 7)) || _tempDateTime.month == 10 && (_tempDateTime.hour + 24 * _tempDateTime.day) < (3 + 24 * (31 - (5 * _tempDateTime.year / 4 + 1) % 7)))
return true;
else
return false;
}
boolean NTPtime::daylightSavingTime(unsigned long _timeStamp) {
strDateTime _tempDateTime;
_tempDateTime = ConvertUnixTimestamp(_timeStamp);
if (_tempDateTime.month < 3 || _tempDateTime.month > 11) return false; //January, february, and december are out.
if (_tempDateTime.month > 3 && _tempDateTime.month < 11) return true; //April to October are in
int previousSunday = _tempDateTime.day - (_tempDateTime.dayofWeek - 1); // dow Sunday input was 1,
// -------------------- March ---------------------------------------
//In march, we are DST if our previous Sunday was = to or after the 8th.
if (_tempDateTime.month == 3 ) { // in march, if previous Sunday is after the 8th, is DST
// unless Sunday and hour < 2am
if ( previousSunday >= 8 ) { // Sunday = 1
// return true if day > 14 or (dow == 1 and hour >= 2)
return ((_tempDateTime.day > 14) ||
((_tempDateTime.dayofWeek == 1 && _tempDateTime.hour >= 2) || _tempDateTime.dayofWeek > 1));
} // end if ( previousSunday >= 8 && _dateTime.dayofWeek > 0 )
else
{
// previousSunday has to be < 8 to get here
//return (previousSunday < 8 && (_tempDateTime.dayofWeek - 1) = 0 && _tempDateTime.hour >= 2)
return false;
} // end else
} // end if (_tempDateTime.month == 3 )
// ------------------------------- November -------------------------------
// gets here only if month = November
//In november we must be before the first Sunday to be dst.
//That means the previous Sunday must be before the 2nd.
if (previousSunday < 1){
// is not true for Sunday after 2am or any day after 1st Sunday any time
return ((_tempDateTime.dayofWeek == 1 && _tempDateTime.hour < 2) || (_tempDateTime.dayofWeek > 1));
//return true;
}else{
// return false unless after first wk and dow = Sunday and hour < 2
return (_tempDateTime.day <8 && _tempDateTime.dayofWeek == 1 && _tempDateTime.hour < 2);
}
}
unsigned long NTPtime::adjustTimeZone(unsigned long _timeStamp, float _timeZone, byte _DayLightSaving) {
strDateTime _tempDateTime;
_timeStamp += (unsigned long)(_timeZone * 3600.0); // adjust timezone
if (_DayLightSaving ==1 && summerTime(_timeStamp)) _timeStamp += 3600; // European Summer time
if (_DayLightSaving ==2 && daylightSavingTime(_timeStamp)) _timeStamp += 3600; // US daylight time
return _timeStamp;
}
strDateTime NTPtime::getNTPtime(float _timeZone, boolean _DayLightSaving) {
int cb;
strDateTime _dateTime;
unsigned long _unixTime = 0;
_dateTime.valid = false;
unsigned long _currentTimeStamp;
if (_sendPhase) {
if (_sentTime && ((millis() - _sentTime) < _sendInterval)) {
return _dateTime;
}
_sendPhase = false;
UDPNTPClient.begin(1337); // Port for NTP receive
memset(_packetBuffer, 0, NTP_PACKET_SIZE);
_packetBuffer[0] = 0b11100011; // LI, Version, Mode
_packetBuffer[1] = 0; // Stratum, or type of clock
_packetBuffer[2] = 6; // Polling Interval
_packetBuffer[3] = 0xEC; // Peer Clock Precision
_packetBuffer[12] = 49;
_packetBuffer[13] = 0x4E;
_packetBuffer[14] = 49;
_packetBuffer[15] = 52;
UDPNTPClient.beginPacket(_NTPserver.c_str(), 123);
UDPNTPClient.write(_packetBuffer, NTP_PACKET_SIZE);
UDPNTPClient.endPacket();
_sentTime = millis();
} else {
cb = UDPNTPClient.parsePacket();
if (cb == 0) {
if ((millis() - _sentTime) > _recvTimeout) {
_sendPhase = true;
_sentTime = 0;
}
} else {
UDPNTPClient.read(_packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
unsigned long highWord = word(_packetBuffer[40], _packetBuffer[41]);
unsigned long lowWord = word(_packetBuffer[42], _packetBuffer[43]);
unsigned long secsSince1900 = highWord << 16 | lowWord;
const unsigned long seventyYears = 2208988800UL;
_unixTime = secsSince1900 - seventyYears;
if (_unixTime > 0) {
_currentTimeStamp = adjustTimeZone(_unixTime, _timeZone, _DayLightSaving);
_dateTime = ConvertUnixTimestamp(_currentTimeStamp);
_dateTime.valid = true;
} else
_dateTime.valid = false;
_sendPhase = true;
}
}
return _dateTime;
}

28
src/ntpt/ntp.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef NTPtime_h
#define NTPtime_h
#include <WiFiUdp.h>
#include "../time/time.h"
class NTPtime {
public:
NTPtime(String NTPtime);
strDateTime getNTPtime(float _timeZone, boolean _DayLightSaving);
bool setSendInterval(unsigned long _sendInterval); // in seconds
bool setRecvTimeout(unsigned long _recvTimeout); // in seconds
static boolean summerTime(unsigned long _timeStamp );
static strDateTime ConvertUnixTimestamp( unsigned long _tempTimeStamp);
static boolean daylightSavingTime(unsigned long _timeStamp);
static unsigned long adjustTimeZone(unsigned long _timeStamp, float _timeZone, byte _DayLightSavingSaving);
private:
bool _sendPhase;
unsigned long _sentTime;
unsigned long _sendInterval;
unsigned long _recvTimeout;
WiFiUDP UDPNTPClient;
};
#endif

93
src/owm/owm.h Normal file
View File

@ -0,0 +1,93 @@
#ifndef OWM_H
#define OWM_H
#include <HTTPClient.h>
#include <ArduinoJson.h>
#define OPENWEATHER_URL(loc, api) ("https://api.openweathermap.org/data/2.5/weather?q=" + loc + "&appid=" + api + "&units=metric")
class OWM{
public:
OWM(){};
OWM(String l,String k) : location(l), key(k){}
StaticJsonDocument<1024> weather;
bool valid_weather = false;
bool update(){
String response = httpGETRequest(OPENWEATHER_URL(location,key).c_str());
DeserializationError error = deserializeJson(weather, response);
if (error) {
valid_weather = false;
return false;
}
if(weather["weather"]==NULL ||weather["main"]==NULL ||
weather["wind"]==NULL || weather["sys"]==NULL ){
valid_weather = false;
return false;
}
valid_weather = true;
return true;
}
String location;
private:
String key;
String httpGETRequest(const char* serverName) {
HTTPClient http;
http.begin(serverName,root_ca);
int httpResponseCode = http.GET();
String payload = "{}";
if (httpResponseCode == HTTP_CODE_OK) {
payload = http.getString();
}
http.end();
return payload;
}
const char* root_ca= \
" -----BEGIN CERTIFICATE-----\n\
MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB\n\
iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl\n\
cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV\n\
BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw\n\
MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV\n\
BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU\n\
aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy\n\
dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK\n\
AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B\n\
3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY\n\
tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/\n\
Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2\n\
VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT\n\
79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6\n\
c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT\n\
Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l\n\
c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee\n\
UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE\n\
Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd\n\
BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G\n\
A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF\n\
Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO\n\
VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3\n\
ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs\n\
8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR\n\
iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze\n\
Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ\n\
XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/\n\
qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB\n\
VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB\n\
L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG\n\
jjxDah2nGN59PRbxYvnKkKj9\n\
-----END CERTIFICATE-----\n";
};
#endif

303
src/sensor/dev/bme280.cpp Normal file
View File

@ -0,0 +1,303 @@
#include "bme280.h"
/****************************************************************/
bool BME280::Initialize(){
bool success(true);
success &= ReadChipID();
if(success){
success &= ReadTrim();
//InitializeFilter(); //If Filter ON
WriteSettings();
}
m_initialized = success;
return m_initialized;
}
/****************************************************************/
bool BME280::ReadChipID(){
uint8_t id[1];
ReadRegister(ID_ADDR, &id[0], 1);
switch(id[0]){
case ChipModel_BME280:
m_chip_model = ChipModel_BME280;
break;
case ChipModel_BMP280:
m_chip_model = ChipModel_BMP280;
break;
default:
m_chip_model = ChipModel_UNKNOWN;
return false;
}
return true;
}
/****************************************************************/
void BME280::WriteSettings(){
// ctrl_hum register. (ctrl_hum[2:0] = Humidity oversampling rate.)
uint8_t ctrlHum = (uint8_t)OSR_X1;
// ctrl_meas register. (ctrl_meas[7:5] = temperature oversampling rate, ctrl_meas[4:2] = pressure oversampling rate, ctrl_meas[1:0] = mode.)
uint8_t ctrlMeas = ((uint8_t)OSR_X1 << 5) | ((uint8_t)OSR_X1 << 2) | (uint8_t)Mode_Forced;
// config register. (config[7:5] = standby time, config[4:2] = filter, ctrl_meas[0] = spi enable.)
uint8_t config = ((uint8_t)StandbyTime_1000ms << 5) | ((uint8_t)Filter_Off << 2) | 0;
WriteRegister(CTRL_HUM_ADDR, ctrlHum);
WriteRegister(CTRL_MEAS_ADDR, ctrlMeas);
WriteRegister(CONFIG_ADDR, config);
}
/****************************************************************/
bool BME280::begin(){
bool success = Initialize();
success &= m_initialized;
return success;
}
/****************************************************************/
bool BME280::reset(){
WriteRegister(RESET_ADDR, RESET_VALUE);
delay(2); //max. startup time according to datasheet
return(begin());
}
/****************************************************************/
bool BME280::ReadTrim(){
uint8_t ord(0);
bool success = true;
// Temp. Dig
success &= ReadRegister(TEMP_DIG_ADDR, &m_dig[ord], TEMP_DIG_LENGTH);
ord += TEMP_DIG_LENGTH;
// Pressure Dig
success &= ReadRegister(PRESS_DIG_ADDR, &m_dig[ord], PRESS_DIG_LENGTH);
ord += PRESS_DIG_LENGTH;
// Humidity Dig 1
success &= ReadRegister(HUM_DIG_ADDR1, &m_dig[ord], HUM_DIG_ADDR1_LENGTH);
ord += HUM_DIG_ADDR1_LENGTH;
// Humidity Dig 2
success &= ReadRegister(HUM_DIG_ADDR2, &m_dig[ord], HUM_DIG_ADDR2_LENGTH);
ord += HUM_DIG_ADDR2_LENGTH;
#ifdef DEBUG_ON
Serial.print("Dig: ");
for(int i = 0; i < 32; ++i){
Serial.print(m_dig[i], HEX);
Serial.print(" ");
}
Serial.println();
#endif
return success && ord == DIG_LENGTH;
}
/****************************************************************/
bool BME280::ReadData(int32_t data[SENSOR_DATA_LENGTH]){
bool success;
uint8_t buffer[SENSOR_DATA_LENGTH];
WriteSettings(); //IF Forced Mode
success = ReadRegister(PRESS_ADDR, buffer, SENSOR_DATA_LENGTH);
for(int i = 0; i < SENSOR_DATA_LENGTH; ++i){
data[i] = static_cast<int32_t>(buffer[i]);
}
return success;
}
/****************************************************************/
float BME280::CalculateTemperature(int32_t raw,int32_t& t_fine){
// Code based on calibration algorthim provided by Bosch.
int32_t var1, var2, final;
uint16_t dig_T1 = (m_dig[1] << 8) | m_dig[0];
int16_t dig_T2 = (m_dig[3] << 8) | m_dig[2];
int16_t dig_T3 = (m_dig[5] << 8) | m_dig[4];
var1 = ((((raw >> 3) - ((int32_t)dig_T1 << 1))) * ((int32_t)dig_T2)) >> 11;
var2 = (((((raw >> 4) - ((int32_t)dig_T1)) * ((raw >> 4) - ((int32_t)dig_T1))) >> 12) * ((int32_t)dig_T3)) >> 14;
t_fine = var1 + var2;
final = (t_fine * 5 + 128) >> 8;
return final/100.0;
}
/****************************************************************/
float BME280::CalculateHumidity
(
int32_t raw,
int32_t t_fine
)
{
// Code based on calibration algorthim provided by Bosch.
int32_t var1;
uint8_t dig_H1 = m_dig[24];
int16_t dig_H2 = (m_dig[26] << 8) | m_dig[25];
uint8_t dig_H3 = m_dig[27];
int16_t dig_H4 = ((int8_t)m_dig[28] * 16) | (0x0F & m_dig[29]);
int16_t dig_H5 = ((int8_t)m_dig[30] * 16) | ((m_dig[29] >> 4) & 0x0F);
int8_t dig_H6 = m_dig[31];
var1 = (t_fine - ((int32_t)76800));
var1 = (((((raw << 14) - (((int32_t)dig_H4) << 20) - (((int32_t)dig_H5) * var1)) +
((int32_t)16384)) >> 15) * (((((((var1 * ((int32_t)dig_H6)) >> 10) * (((var1 *
((int32_t)dig_H3)) >> 11) + ((int32_t)32768))) >> 10) + ((int32_t)2097152)) *
((int32_t)dig_H2) + 8192) >> 14));
var1 = (var1 - (((((var1 >> 15) * (var1 >> 15)) >> 7) * ((int32_t)dig_H1)) >> 4));
var1 = (var1 < 0 ? 0 : var1);
var1 = (var1 > 419430400 ? 419430400 : var1);
return ((uint32_t)(var1 >> 12))/1024.0;
}
/****************************************************************/
float BME280::CalculatePressure(int32_t raw,int32_t t_fine,PresUnit unit){
// Code based on calibration algorthim provided by Bosch.
int64_t var1, var2, pressure;
float final;
uint16_t dig_P1 = (m_dig[7] << 8) | m_dig[6];
int16_t dig_P2 = (m_dig[9] << 8) | m_dig[8];
int16_t dig_P3 = (m_dig[11] << 8) | m_dig[10];
int16_t dig_P4 = (m_dig[13] << 8) | m_dig[12];
int16_t dig_P5 = (m_dig[15] << 8) | m_dig[14];
int16_t dig_P6 = (m_dig[17] << 8) | m_dig[16];
int16_t dig_P7 = (m_dig[19] << 8) | m_dig[18];
int16_t dig_P8 = (m_dig[21] << 8) | m_dig[20];
int16_t dig_P9 = (m_dig[23] << 8) | m_dig[22];
var1 = (int64_t)t_fine - 128000;
var2 = var1 * var1 * (int64_t)dig_P6;
var2 = var2 + ((var1 * (int64_t)dig_P5) << 17);
var2 = var2 + (((int64_t)dig_P4) << 35);
var1 = ((var1 * var1 * (int64_t)dig_P3) >> 8) + ((var1 * (int64_t)dig_P2) << 12);
var1 = (((((int64_t)1) << 47) + var1)) * ((int64_t)dig_P1) >> 33;
if (var1 == 0) { return NAN; } // Don't divide by zero.
pressure = 1048576 - raw;
pressure = (((pressure << 31) - var2) * 3125)/var1;
var1 = (((int64_t)dig_P9) * (pressure >> 13) * (pressure >> 13)) >> 25;
var2 = (((int64_t)dig_P8) * pressure) >> 19;
pressure = ((pressure + var1 + var2) >> 8) + (((int64_t)dig_P7) << 4);
final = ((uint32_t)pressure)/256.0;
// Conversion units courtesy of www.endmemo.com.
switch(unit){
case PresUnit_hPa: /* hPa */
final /= 100.0;
break;
case PresUnit_atm: /* atm */
final /= 101324.99766353; /* final pa * 1 atm/101324.99766353Pa */
break;
case PresUnit_bar: /* bar */
final /= 100000.0; /* final pa * 1 bar/100kPa */
break;
default: /* Pa (case: 0) */
break;
}
return final;
}
/****************************************************************/
float BME280::temp(){
if(!m_initialized) return 0;
int32_t data[8];
int32_t t_fine;
if(!ReadData(data)){ return NAN; }
uint32_t rawTemp = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4);
return CalculateTemperature(rawTemp, t_fine);
}
/****************************************************************/
float BME280::pres(PresUnit unit){
if(!m_initialized) return 0;
int32_t data[8];
int32_t t_fine;
if(!ReadData(data)){ return NAN; }
uint32_t rawTemp = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4);
uint32_t rawPressure = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4);
CalculateTemperature(rawTemp, t_fine);
return CalculatePressure(rawPressure, t_fine);
}
/****************************************************************/
float BME280::hum(){
if(!m_initialized) return 0;
int32_t data[8];
int32_t t_fine;
if(!ReadData(data)){ return NAN; }
uint32_t rawTemp = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4);
uint32_t rawHumidity = (data[6] << 8) | data[7];
CalculateTemperature(rawTemp, t_fine);
return CalculateHumidity(rawHumidity, t_fine);
}
/****************************************************************/
void BME280::read(float& pressure,float& temp,float& humidity,PresUnit presUnit){
if(!m_initialized) return;
int32_t data[8];
int32_t t_fine;
if(!ReadData(data)){
pressure = temp = humidity = NAN;
return;
}
uint32_t rawPressure = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4);
uint32_t rawTemp = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4);
uint32_t rawHumidity = (data[6] << 8) | data[7];
temp = CalculateTemperature(rawTemp, t_fine);
pressure = CalculatePressure(rawPressure, t_fine, presUnit);
humidity = CalculateHumidity(rawHumidity, t_fine);
}
/****************************************************************/
BME280::ChipModel BME280::chipModel(){
return m_chip_model;
}
/****************************************************************/
bool BME280::WriteRegister(uint8_t addr, uint8_t data){
i2c->beginTransmission(BME280_ADDRESS);
i2c->write(addr);
i2c->write(data);
i2c->endTransmission();
return true;
}
/****************************************************************/
bool BME280::ReadRegister
(
uint8_t addr,
uint8_t data[],
uint8_t length
)
{
uint8_t ord(0);
i2c->beginTransmission(BME280_ADDRESS);
i2c->write(addr);
i2c->endTransmission();
i2c->requestFrom(static_cast<uint8_t>(BME280_ADDRESS), length);
while(i2c->available())
{
data[ord++] = i2c->read();
}
return ord == length;
}

124
src/sensor/dev/bme280.h Normal file
View File

@ -0,0 +1,124 @@
#ifndef TG_BME_280_H
#define TG_BME_280_H
#include <Wire.h>
#define BME280_ADDRESS (0x76)
#define BME280_ADDRESS_ALTERNATE (0x77)
//////////////////////////////////////////////////////////////////
/// BME280 - Driver class for Bosch Bme280 sensor
///
/// Based on the data sheet provided by Bosch for
/// the Bme280 environmental sensor.
///
class BME280{
public:
enum PresUnit{
PresUnit_Pa,
PresUnit_hPa,
PresUnit_atm,
PresUnit_bar,
};
enum OSR{
OSR_Off = 0,
OSR_X1 = 1,
OSR_X2 = 2,
OSR_X4 = 3,
OSR_X8 = 4,
OSR_X16 = 5
};
enum Mode{
Mode_Sleep = 0,
Mode_Forced = 1,
Mode_Normal = 3
};
enum StandbyTime{
StandbyTime_500us = 0,
StandbyTime_62500us = 1,
StandbyTime_125ms = 2,
StandbyTime_250ms = 3,
StandbyTime_50ms = 4,
StandbyTime_1000ms = 5,
StandbyTime_10ms = 6,
StandbyTime_20ms = 7
};
enum Filter{
Filter_Off = 0,
Filter_2 = 1,
Filter_4 = 2,
Filter_8 = 3,
Filter_16 = 4
};
enum ChipModel{
ChipModel_UNKNOWN = 0,
ChipModel_BMP280 = 0x58,
ChipModel_BME280 = 0x60
};
BME280(TwoWire* i2c): m_initialized(false), i2c(i2c){}
ChipModel chipModel();
bool begin();
bool reset();
float temp();
float pres(PresUnit unit = PresUnit_hPa);
float hum();
void read(float& pressure,float& temperature,float& humidity, PresUnit presUnit = PresUnit_hPa);
protected:
bool Initialize();
private:
TwoWire* i2c;
uint8_t m_dig[32];
ChipModel m_chip_model;
bool m_initialized;
static const uint8_t CTRL_HUM_ADDR = 0xF2;
static const uint8_t CTRL_MEAS_ADDR = 0xF4;
static const uint8_t CONFIG_ADDR = 0xF5;
static const uint8_t PRESS_ADDR = 0xF7;
static const uint8_t TEMP_ADDR = 0xFA;
static const uint8_t HUM_ADDR = 0xFD;
static const uint8_t TEMP_DIG_ADDR = 0x88;
static const uint8_t PRESS_DIG_ADDR = 0x8E;
static const uint8_t HUM_DIG_ADDR1 = 0xA1;
static const uint8_t HUM_DIG_ADDR2 = 0xE1;
static const uint8_t ID_ADDR = 0xD0;
static const uint8_t RESET_ADDR = 0xE0;
static const uint8_t RESET_VALUE = 0xB6;
static const uint8_t TEMP_DIG_LENGTH = 6;
static const uint8_t PRESS_DIG_LENGTH = 18;
static const uint8_t HUM_DIG_ADDR1_LENGTH = 1;
static const uint8_t HUM_DIG_ADDR2_LENGTH = 7;
static const uint8_t DIG_LENGTH = 32;
static const uint8_t SENSOR_DATA_LENGTH = 8;
void WriteSettings();
bool ReadChipID();
bool ReadTrim();
bool ReadData(int32_t data[8]);
bool WriteRegister(uint8_t addr,uint8_t data);
bool ReadRegister(uint8_t addr,uint8_t data[],uint8_t length);
float CalculateTemperature(int32_t raw,int32_t& t_fine);
float CalculateHumidity(int32_t raw,int32_t t_fine);
float CalculatePressure(int32_t raw,int32_t t_fine,PresUnit unit = PresUnit_hPa);
};
#endif // TG_BME_280_H

View File

137
src/sensor/dev/ltr390.cpp Normal file
View File

@ -0,0 +1,137 @@
#include "ltr390.h"
/****************************************************************/
bool LTR390::Initialize(){
bool success(true);
success &= ReadChipID();
if(success){
WriteSettings();
}
m_initialized = success;
return m_initialized;
}
/****************************************************************/
bool LTR390::ReadChipID(){
uint8_t id;
ReadRegister(ID_ADDR, &id, 1);
switch(id){
case ChipModel_LTR390_REV0:
m_chip_model = ChipModel_LTR390_REV0;
break;
case ChipModel_LTR390_REV1:
m_chip_model = ChipModel_LTR390_REV1;
break;
case ChipModel_LTR390_REV2:
m_chip_model = ChipModel_LTR390_REV2;
break;
case ChipModel_LTR390_REV3:
m_chip_model = ChipModel_LTR390_REV3;
break;
default:
m_chip_model = ChipModel_UNKNOWN;
return false;
}
return true;
}
/****************************************************************/
void LTR390::WriteSettings(){
uint8_t ctrlMeas = LTR390_RESOLUTION_16BIT | LTR390_RATE_1000;
uint8_t ctrlGain = LTR390_GAIN_3;
uint8_t ctrlMode = LTR390_ON | LTR390_MODE_UVS;
WriteRegister(MEAS_RATE, ctrlMeas);
WriteRegister(GAIN, ctrlGain);
WriteRegister(MAIN_CTRL, ctrlMode);
}
/****************************************************************/
bool LTR390::begin(){
bool success = Initialize();
success &= m_initialized;
return success;
}
/****************************************************************/
bool LTR390::reset(){
WriteRegister(RESET_ADDR, RESET_VALUE);
delay(10); //max. startup time according to datasheet
return(begin());
}
/****************************************************************/
bool LTR390::ReadData(int32_t data[SENSOR_DATA_LENGTH], uint32_t addr){
bool success;
uint8_t buffer[SENSOR_DATA_LENGTH];
// WriteSettings();
success = ReadRegister(addr, buffer, SENSOR_DATA_LENGTH);
for(int i = 0; i < SENSOR_DATA_LENGTH; ++i){
data[i] = static_cast<int32_t>(buffer[i]);
}
return success;
}
/****************************************************************/
uint32_t LTR390::uv(){
if(!m_initialized) return 0;
int32_t data[SENSOR_DATA_LENGTH];
if(!ReadData(data, UVSDATA)){ return 0; }
uint32_t rawUV = (data[2] << 16) | (data[1] << 8) | (data[0]);
return rawUV;
}
void LTR390::read(uint32_t& uvs, uint32_t& uvi){
if(!m_initialized) return;
uvs = uv();
uvi = (uvs/ (3*0.25))*3;
}
/****************************************************************/
LTR390::ChipModel LTR390::chipModel(){
return m_chip_model;
}
/****************************************************************/
bool LTR390::WriteRegister(uint8_t addr, uint8_t data){
i2c->beginTransmission(LTR390_ADDRESS);
i2c->write(addr);
i2c->write(data);
i2c->endTransmission();
return true;
}
/****************************************************************/
bool LTR390::ReadRegister(uint8_t addr,uint8_t data[],uint8_t length){
uint8_t ord(0);
i2c->beginTransmission(LTR390_ADDRESS);
i2c->write(addr);
i2c->endTransmission();
i2c->requestFrom(static_cast<uint8_t>(LTR390_ADDRESS), length);
while(i2c->available()){
data[ord++] = i2c->read();
}
return ord == length;
}

110
src/sensor/dev/ltr390.h Normal file
View File

@ -0,0 +1,110 @@
#ifndef TG_LTR_390_H
#define TG_LTR_390_H
#include <Wire.h>
#define LTR390_ADDRESS (0x53)
//////////////////////////////////////////////////////////////////
/// LTR390 - Driver class for Ltr390 sensor
///
/// Based on the data sheet provided
///
class LTR390{
public:
typedef enum {
LTR390_ON = 0x2,
LTR390_MODE_ALS = 0x0,
LTR390_MODE_UVS = 0x8,
} ltr390_mode_t;
/*! @brief Sensor gain for UV or ALS */
typedef enum {
LTR390_GAIN_1 = 0,
LTR390_GAIN_3 = 1,
LTR390_GAIN_6 = 2,
LTR390_GAIN_9 = 3,
LTR390_GAIN_18 = 4,
} ltr390_gain_t;
/*! @brief Measurement resolution (higher res means slower reads!) */
typedef enum {
LTR390_RESOLUTION_20BIT = 0x0,
LTR390_RESOLUTION_19BIT = 0x10,
LTR390_RESOLUTION_18BIT = 0x20,
LTR390_RESOLUTION_17BIT = 0x30,
LTR390_RESOLUTION_16BIT = 0x40,
LTR390_RESOLUTION_13BIT = 0x50,
} ltr390_resolution_t;
/*! @brief Measurement resolution (higher res means slower reads!) */
typedef enum {
LTR390_RATE_25 = 0x0,
LTR390_RATE_50 = 0x1,
LTR390_RATE_100 = 0x2,
LTR390_RATE_200 = 0x3,
LTR390_RATE_500 = 0x4,
LTR390_RATE_1000 = 0x5,
LTR390_RATE_2000 = 0x6,
} ltr390_rate_t;
enum ChipModel{
ChipModel_UNKNOWN = 0,
ChipModel_LTR390_REV0 = 0xB0,
ChipModel_LTR390_REV1 = 0xB1,
ChipModel_LTR390_REV2 = 0xB2,
ChipModel_LTR390_REV3 = 0xB3,
};
LTR390(TwoWire* i2c): m_initialized(false),i2c(i2c){}
ChipModel chipModel();
bool begin();
bool reset();
uint32_t uv();
void read(uint32_t& uv, uint32_t& uvi);
protected:
bool Initialize();
void InitializeFilter();
private:
TwoWire* i2c;
ChipModel m_chip_model;
bool m_initialized;
static const uint8_t MAIN_CTRL = 0x00; ///< Main control register
static const uint8_t MEAS_RATE = 0x04; ///< Resolution and data rate
static const uint8_t GAIN = 0x05; ///< ALS and UVS gain range
static const uint8_t ID_ADDR = 0x06; ///< Part id/revision register
static const uint8_t MAIN_STATUS = 0x07; ///< Main status register
static const uint8_t ALSDATA = 0x0D; ///< ALS data lowest byte
static const uint8_t UVSDATA = 0x10; ///< UVS data lowest byte
static const uint8_t INT_CFG = 0x19; ///< Interrupt configuration
static const uint8_t INT_PST = 0x1A; ///< Interrupt persistance config
static const uint8_t THRESH_UP = 0x21; ///< Upper threshold, low byte
static const uint8_t THRESH_LOW = 0x24; ///< Lower threshold, low byte
static const uint8_t RESET_ADDR = 0x00; ///< Reset Address
static const uint8_t RESET_VALUE = 0x10; ///< Reset value
static const uint8_t SENSOR_DATA_LENGTH = 3; //For 16 bit resolution !
bool ReadChipID();
bool ReadTrim();
bool ReadData(int32_t data[SENSOR_DATA_LENGTH], uint32_t addr);
bool ReadRegister(uint8_t addr,uint8_t data[],uint8_t length);
void WriteSettings();
bool WriteRegister(uint8_t addr,uint8_t data);
};
#endif

131
src/sensor/dev/pmsa003.cpp Normal file
View File

@ -0,0 +1,131 @@
#include "pmsa003.h"
#include <Arduino.h>
/****************************************************************/
bool PMSA003::Initialize(){
bool success(true);
success &= ReadChipID();
if(success) success = WriteSettings();
m_initialized = success;
return m_initialized;
}
/****************************************************************/
bool PMSA003::ReadChipID(){
uint8_t id = 0;
ReadRegister(REG_VERSION, &id, 1);
Serial.print("PMSSA DEV: ");Serial.println(id);
// switch(id){
// case ChipModel_PMSA003_REV0:
// m_chip_model = ChipModel_PMSA003_REV0;
// break;
// default:
// m_chip_model = ChipModel_UNKNOWN;
// return false;
// }
return true;
}
/****************************************************************/
bool PMSA003::WriteSettings(){
return true; //No settings
}
/****************************************************************/
bool PMSA003::begin(){
bool success = Initialize();
success &= m_initialized;
return success;
}
/****************************************************************/
bool PMSA003::reset(){
return true; //Cannot be reset
}
/****************************************************************/
bool PMSA003::ReadData(uint8_t data[SENSOR_DATA_LENGTH]){
ReadRegister(REG_Char1,data,SENSOR_DATA_LENGTH);
if (data[0] != 0x42 || data[1] != 0x4d) return false;
uint16_t sum = 0;
for (uint8_t i = 0; i < 30; i++) sum += data[i];
uint16_t checksum = data[30]<<8 | data[31];
if (sum != checksum) return false;
return true;
}
/****************************************************************/
void PMSA003::read(PM25_AQI_Data& data){
if(!m_initialized) return;
uint8_t rd[SENSOR_DATA_LENGTH];
if(!ReadData(rd)) return;
uint16_t md[16] = {0};
for (uint8_t i = 0; i < 15; i++) {
md[i] = rd[i * 2] << 8;
md[i] |= rd[i * 2 + 1];
}
data.pm10_standard = md[2];
data.pm25_standard = md[3];
data.pm100_standard = md[4];
data.pm10_env = md[5];
data.pm25_env = md[6];
data.pm100_env = md[7];
data.particles_03um = md[8];
data.particles_05um = md[9];
data.particles_10um = md[10];
data.particles_25um = md[11];
data.particles_50um = md[12];
data.particles_100um = md[13];
}
/****************************************************************/
PMSA003::ChipModel PMSA003::chipModel(){
return m_chip_model;
}
/****************************************************************/
bool PMSA003::WriteRegister(uint16_t addr, uint8_t data){
i2c->beginTransmission(PMSA003_ADDRESS);
i2c->write(highByte(addr));
i2c->write(lowByte(addr));
i2c->write(data);
i2c->endTransmission();
return true;
}
/****************************************************************/
bool PMSA003::ReadRegister(uint8_t addr,uint8_t data[],uint8_t length){
uint8_t ord(0);
i2c->beginTransmission(PMSA003_ADDRESS);
i2c->write(addr);
i2c->endTransmission();
i2c->requestFrom(static_cast<uint8_t>(PMSA003_ADDRESS), length);
while(i2c->available()){
data[ord++] = i2c->read();
}
return ord == length;
}

106
src/sensor/dev/pmsa003.h Normal file
View File

@ -0,0 +1,106 @@
#ifndef TG_PMSA003_H
#define TG_PMSA003_H
#include <Arduino.h>
#include <Wire.h>
#define PMSA003_ADDRESS (0x12)
#define SENSOR_DATA_LENGTH 32
typedef struct PMSAQIdata {
uint16_t pm10_standard, ///< Standard PM1.0
pm25_standard, ///< Standard PM2.5
pm100_standard; ///< Standard PM10.0
uint16_t pm10_env, ///< Environmental PM1.0
pm25_env, ///< Environmental PM2.5
pm100_env; ///< Environmental PM10.0
uint16_t particles_03um, ///< 0.3um Particle Count
particles_05um, ///< 0.5um Particle Count
particles_10um, ///< 1.0um Particle Count
particles_25um, ///< 2.5um Particle Count
particles_50um, ///< 5.0um Particle Count
particles_100um; ///< 10.0um Particle Count
} PM25_AQI_Data;
//////////////////////////////////////////////////////////////////
/// PMSA003 - Driver class for PMSA003 sensor
///
/// Based on the data sheet provided
///
class PMSA003{
public:
enum ChipModel{
ChipModel_UNKNOWN = 0,
ChipModel_PMSA003_REV0 = 0x10
};
PMSA003(TwoWire* i2c): m_initialized(false),i2c(i2c){}
ChipModel chipModel();
bool begin();
bool reset();
void read(PM25_AQI_Data& data);
protected:
bool Initialize();
private:
bool m_initialized;
ChipModel m_chip_model;
TwoWire* i2c;
static const uint8_t REG_Char1 = 0x00; // 0x42
static const uint8_t REG_Char2 = 0x01; // 0x4d
static const uint8_t REG_FrameH = 0x02; // Frame length = 2x13 + 2
static const uint8_t REG_FrameL = 0x03; // --
static const uint8_t REG_d1h = 0x04; // PM1.0 ug/m3 (standard)
static const uint8_t REG_d1l = 0x05;
static const uint8_t REG_d2h = 0x06; // PM2.5 ug/m3 (standard)
static const uint8_t REG_d2l = 0x07;
static const uint8_t REG_d3h = 0x08; // PM10 ug/m3 (standard)
static const uint8_t REG_d3l = 0x09;
static const uint8_t REG_d4h = 0x0A; // PM1.0 ug/m3 (atmospheric env)
static const uint8_t REG_d4l = 0x0B;
static const uint8_t REG_d5h = 0x0C; // PM2.5 ug/m3 (atmospheric env)
static const uint8_t REG_d5l = 0x0D;
static const uint8_t REG_d6h = 0x0E; // PM10 ug/m3 (atmospheric env)
static const uint8_t REG_d6l = 0x0F;
static const uint8_t REG_d7h = 0x10; // >0.3um/0.1L
static const uint8_t REG_d7l = 0x11;
static const uint8_t REG_d8h = 0x12; // >0.5um/0.1L
static const uint8_t REG_d8l = 0x13;
static const uint8_t REG_d9h = 0x14; // >1.0um/0.1L
static const uint8_t REG_d9l = 0x15;
static const uint8_t REG_d10h = 0x16; // >2.5um/0.1L
static const uint8_t REG_d10l = 0x17;
static const uint8_t REG_d11h = 0x18; // >5.0um/0.1L
static const uint8_t REG_d11l = 0x19;
static const uint8_t REG_d12h = 0x1A; // >10.0um/0.1L
static const uint8_t REG_d12l = 0x1B;
static const uint8_t REG_VERSION = 0x1C; // Verion
static const uint8_t REG_ERR = 0x1D; // Error
static const uint8_t REG_checkh = 0x1E; // Checksum
static const uint8_t REG_checkl = 0x1D;
bool ReadChipID();
bool ReadRegister(uint8_t addr,uint8_t data[],uint8_t length);
bool ReadData(uint8_t data[SENSOR_DATA_LENGTH]);
bool WriteSettings();
bool WriteRegister(uint16_t addr,uint8_t data);
};
#endif

180
src/sensor/dev/sgp40.cpp Normal file
View File

@ -0,0 +1,180 @@
#include "sgp40.h"
/****************************************************************/
bool SGP40::Initialize(){
bool success(true);
success &= ReadChipID();
VocAlgorithm_init(&vocAlgorithmParameters);
if(success) success = WriteSettings();
m_initialized = success;
return m_initialized;
}
/****************************************************************/
bool SGP40::ReadChipID(){
uint8_t id = 0;
//ReadRegister(ID_ADDR, &id, 1);
switch(id){
default:
m_chip_model = ChipModel_UNKNOWN;
return false;
}
return true;
}
/****************************************************************/
bool SGP40::WriteSettings(){
uint8_t data[3];
uint8_t ord(0);
i2c->beginTransmission(SGP40_ADDRESS);
i2c->write(highByte(REG_MEASURE_TEST));
i2c->write(lowByte(REG_MEASURE_TEST));
i2c->endTransmission();
delay(320);
i2c->requestFrom(static_cast<uint8_t>(SGP40_ADDRESS), uint8_t(3));
while(i2c->available()){
data[ord++] = i2c->read();
}
if(ord != 3) return false;
uint16_t results = (data[0] << 8) | data[1];
return (results == test_pass);
}
/****************************************************************/
bool SGP40::begin(){
bool success = Initialize();
success &= m_initialized;
return success;
}
/****************************************************************/
bool SGP40::reset(){
WriteRegister(RESET_ADDR, 0x02);
delay(10); //max. startup time according to datasheet
return(begin());
}
/****************************************************************/
bool SGP40::ReadData(uint8_t data[SENSOR_DATA_LENGTH], float RH, float T){
bool success;
uint8_t ord(0);
RH = (RH>100?100: (RH<0?0:RH));
T = (T>130?130: (T<-45?-45:T));
uint16_t RH_ticks = (uint16_t)(RH * 65535 / 100); // Convert RH from %RH to ticks
uint16_t T_ticks = (uint16_t)((T + 45) * 65535 / 175); // Convert T from DegC to ticks
i2c->beginTransmission(SGP40_ADDRESS);
i2c->write(highByte(REG_MEASURE_RAW));
i2c->write(lowByte(REG_MEASURE_RAW));
i2c->write(highByte(RH_ticks));
i2c->write(lowByte(RH_ticks));
i2c->write(CRC8(RH_ticks));
i2c->write(highByte(T_ticks));
i2c->write(lowByte(T_ticks));
i2c->write(CRC8(T_ticks));
i2c->endTransmission();
delay(30);
i2c->requestFrom(SGP40_ADDRESS, SENSOR_DATA_LENGTH);
while(i2c->available()){
data[ord++] = i2c->read();
}
return success;
}
/****************************************************************/
int32_t SGP40::voc(float RH, float T){
if(!m_initialized) return 0;
uint8_t data[SENSOR_DATA_LENGTH];
ReadData(data, RH, T);
uint16_t results = (data[0]<<8) | data[1];
int32_t voci = 0;
VocAlgorithm_process(&vocAlgorithmParameters, results, &voci);
return voci;
}
void SGP40::read(int32_t& voc, float RH, float T){
if(!m_initialized) return;
uint8_t data[SENSOR_DATA_LENGTH];
ReadData(data, RH, T);
uint16_t results = (data[0]<<8) | data[1];
VocAlgorithm_process(&vocAlgorithmParameters, results, &voc);
return;
}
/****************************************************************/
SGP40::ChipModel SGP40::chipModel(){
return m_chip_model;
}
/****************************************************************/
bool SGP40::WriteRegister(uint16_t addr, uint8_t data){
i2c->beginTransmission(SGP40_ADDRESS);
i2c->write(highByte(addr));
i2c->write(lowByte(addr));
i2c->write(data);
i2c->endTransmission();
return true;
}
/****************************************************************/
bool SGP40::ReadRegister(uint8_t addr,uint8_t data[],uint8_t length){
uint8_t ord(0);
i2c->beginTransmission(SGP40_ADDRESS);
i2c->write(addr);
i2c->endTransmission();
i2c->requestFrom(static_cast<uint8_t>(SGP40_ADDRESS), length);
while(i2c->available()){
data[ord++] = i2c->read();
}
return ord == length;
}
uint8_t SGP40::CRC8(uint16_t data){
uint8_t crc = 0xFF;
crc ^= (data >> 8);
for (uint8_t i = 0 ; i < 8 ; i++){
if ((crc & 0x80) != 0) crc = (uint8_t)((crc << 1) ^ 0x31);
else crc <<= 1;
}
crc ^= (uint8_t)data;
for (uint8_t i = 0 ; i < 8 ; i++){
if ((crc & 0x80) != 0) crc = (uint8_t)((crc << 1) ^ 0x31);
else crc <<= 1;
}
return crc;
}

67
src/sensor/dev/sgp40.h Normal file
View File

@ -0,0 +1,67 @@
#ifndef TG_SGP40_H
#define TG_SGP40_H
#include <Arduino.h>
#include <Wire.h>
#include "../voc.h"
#define SGP40_ADDRESS (0x59)
#define SENSOR_DATA_LENGTH 3
//////////////////////////////////////////////////////////////////
/// SGP40 - Driver class for SGP40 sensor
///
/// Based on the data sheet provided
///
class SGP40{
public:
enum ChipModel{
ChipModel_UNKNOWN = 0
};
SGP40(TwoWire* i2c): m_initialized(false),i2c(i2c){}
ChipModel chipModel();
bool begin();
bool reset();
int32_t voc(float RH = 50, float T = 20);
void read(int32_t& voc, float RH = 50, float T = 20);
protected:
bool Initialize();
private:
bool m_initialized;
ChipModel m_chip_model;
TwoWire* i2c;
VocAlgorithmParams vocAlgorithmParameters;
static const uint16_t REG_MEASURE_RAW = 0x260F;
static const uint16_t REG_MEASURE_TEST = 0x280E;
static const uint16_t REG_HEATER_OFF = 0x3615;
static const uint16_t RESET_ADDR = 0x0006;
static const uint16_t test_pass = 0xD400;
static const uint16_t test_fail = 0x4B00;
bool ReadChipID();
bool ReadRegister(uint8_t addr,uint8_t data[],uint8_t length);
bool ReadData(uint8_t data[SENSOR_DATA_LENGTH], float RH, float T);
bool WriteSettings();
bool WriteRegister(uint16_t addr,uint8_t data);
uint8_t CRC8(uint16_t twoBytes);
};
#endif

228
src/sensor/dev/tsl25911.cpp Normal file
View File

@ -0,0 +1,228 @@
#include "tsl25911.h"
#define ENABLE_POWEROFF (0x00) ///< Flag for ENABLE register to disable
#define ENABLE_POWERON (0x01) ///< Flag for ENABLE register to enable
#define ENABLE_AEN (0x02) ///< ALS Enable. This field activates ALS function. Writing a one
///< activates the ALS. Writing a zero disables the ALS.
#define ENABLE_AIEN (0x10) ///< ALS Interrupt Enable. When asserted permits ALS interrupts to be
///< generated, subject to the persist filter.
#define ENABLE_NPIEN (0x80) ///< No Persist Interrupt Enable. When asserted NP Threshold conditions
///< will generate an interrupt, bypassing the persist filter
#define LUX_DF (408.0F) ///< Lux cooefficient
#define LUX_COEFB (1.64F) ///< CH0 coefficient
#define LUX_COEFC (0.59F) ///< CH1 coefficient A
#define LUX_COEFD (0.86F) ///< CH2 coefficient B
/****************************************************************/
bool TSL25911::Initialize(){
bool success(true);
success &= ReadChipID();
if(success) WriteSettings();
m_initialized = success;
return m_initialized;
}
/****************************************************************/
bool TSL25911::ReadChipID(){
uint8_t id;
ReadRegister(ID_ADDR, &id, 1);
switch(id){
case ChipModel_TSL25911_REV0:
m_chip_model = ChipModel_TSL25911_REV0;
default:
m_chip_model = ChipModel_UNKNOWN;
return false;
}
return true;
}
/****************************************************************/
void TSL25911::WriteSettings(){
WriteRegister(REG_ENABLE, ENABLE_POWERON | ENABLE_AEN);
WriteRegister(REG_CTRL, integration | gain);
WriteRegister(REG_ENABLE,ENABLE_POWEROFF);
}
/****************************************************************/
bool TSL25911::begin(){
bool success = Initialize();
success &= m_initialized;
return success;
}
/****************************************************************/
bool TSL25911::reset(){
//Write RST REG ?
delay(10); //max. startup time according to datasheet
return(begin());
}
/****************************************************************/
bool TSL25911::ReadData(uint32_t data[SENSOR_DATA_LENGTH]){
bool success;
success &= WriteRegister(REG_ENABLE, ENABLE_POWERON | ENABLE_AEN);
for (uint8_t d = 0; d <= integration; d++) // Wait x ms for ADC to complete
delay(120);
success &= ReadRegister(CHAN0_LOW,static_cast<uint8_t*>(static_cast<void*>(&data[0])),2);
success &= ReadRegister(CHAN1_LOW,static_cast<uint8_t*>(static_cast<void*>(&data[2])),2);
success &= WriteRegister(REG_ENABLE,ENABLE_POWEROFF);
return success;
}
/****************************************************************/
float ComputeLux(uint16_t ch0, uint16_t ch1, float atime, float again) {
float cpl, lux1, lux2, lux;
if ((ch0 == 0xFFFF) | (ch1 == 0xFFFF)) return -1;
cpl = (atime * again) / LUX_DF;
lux = (((float)ch0 - (float)ch1)) * (1.0F - ((float)ch1 / (float)ch0)) / cpl;
return lux;
}
uint16_t TSL25911::lumV(){
if(!m_initialized) return 0;
uint32_t data;
if(ReadData(&data))
return ((data & 0xFFFF) - (data >> 16));
return 0;
}
uint16_t TSL25911::lumIR(){
if(!m_initialized) return 0;
uint32_t data;
if(ReadData(&data))
return (data >> 16);
return 0;
}
uint16_t TSL25911::lumF(){
if(!m_initialized) return 0;
uint32_t data;
if(ReadData(&data))
return (data & 0xFFFF);
return 0;
}
float TSL25911::lumLux(){
if(!m_initialized) return 0;
uint32_t data;
if(ReadData(&data)){
return ComputeLux(data&0xFFFF, data>>16,again(),atime());
}
return 0;
}
void TSL25911::read(uint16_t& v, uint16_t& ir, uint16_t& f, float& lux){
if(!m_initialized) return;
uint32_t data;
if(ReadData(&data)){
v = ((data & 0xFFFF) - (data >> 16));
ir = (data >> 16);
f = (data & 0xFFFF);
lux = ComputeLux(data&0xFFFF,data>>16,again(),atime());
}
}
/****************************************************************/
TSL25911::ChipModel TSL25911::chipModel(){
return m_chip_model;
}
/****************************************************************/
bool TSL25911::WriteRegister(uint8_t addr, uint8_t data){
i2c->beginTransmission(TSL25911_ADDRESS);
i2c->write(addr);
i2c->write(data);
i2c->endTransmission();
return true;
}
/****************************************************************/
bool TSL25911::ReadRegister(uint8_t addr,uint8_t data[],uint8_t length){
uint8_t ord(0);
i2c->beginTransmission(TSL25911_ADDRESS);
i2c->write(addr);
i2c->endTransmission();
i2c->requestFrom(static_cast<uint8_t>(TSL25911_ADDRESS), length);
while(i2c->available()){
data[ord++] = i2c->read();
}
return ord == length;
}
float TSL25911::again(){
switch (gain) {
case TSL2591_GAIN_LOW:
return 1.0F;
break;
case TSL2591_GAIN_MED:
return 25.0F;
break;
case TSL2591_GAIN_HIGH:
return 428.0F;
break;
case TSL2591_GAIN_MAX:
return 9876.0F;
break;
default:
return 1.0F;
break;
}
}
float TSL25911::atime(){
switch (integration) {
case TSL2591_INTEGRATIONTIME_100MS:
return 100.0F;
break;
case TSL2591_INTEGRATIONTIME_200MS:
return 200.0F;
break;
case TSL2591_INTEGRATIONTIME_300MS:
return 300.0F;
break;
case TSL2591_INTEGRATIONTIME_400MS:
return 400.0F;
break;
case TSL2591_INTEGRATIONTIME_500MS:
return 500.0F;
break;
case TSL2591_INTEGRATIONTIME_600MS:
return 600.0F;
break;
default: // 100ms
return 100.0F;
break;
}
}

121
src/sensor/dev/tsl25911.h Normal file
View File

@ -0,0 +1,121 @@
#ifndef TG_TSL25911_H
#define TG_TSL25911_H
#include <Wire.h>
#define TSL25911_ADDRESS (0x29)
#define SENSOR_DATA_LENGTH 4
//////////////////////////////////////////////////////////////////
/// TSL25911 - Driver class for TSL25911 sensor
///
/// Based on the data sheet provided
///
class TSL25911{
public:
typedef enum {
TSL25911_FULL = 0x0,
TSL25911_IR = 0x1,
TSL25911_VIS = 0x2,
} tls25911_mode_t;
typedef enum {
TSL2591_INTEGRATIONTIME_100MS = 0x00, // 100 millis
TSL2591_INTEGRATIONTIME_200MS = 0x01, // 200 millis
TSL2591_INTEGRATIONTIME_300MS = 0x02, // 300 millis
TSL2591_INTEGRATIONTIME_400MS = 0x03, // 400 millis
TSL2591_INTEGRATIONTIME_500MS = 0x04, // 500 millis
TSL2591_INTEGRATIONTIME_600MS = 0x05, // 600 millis
} tsl2591IntegrationTime_t;
/// Enumeration for the persistance filter (for interrupts)
typedef enum {// bit 7:4: 0
TSL2591_PERSIST_EVERY = 0x00, // Every ALS cycle generates an interrupt
TSL2591_PERSIST_ANY = 0x01, // Any value outside of threshold range
TSL2591_PERSIST_2 = 0x02, // 2 consecutive values out of range
TSL2591_PERSIST_3 = 0x03, // 3 consecutive values out of range
TSL2591_PERSIST_5 = 0x04, // 5 consecutive values out of range
TSL2591_PERSIST_10 = 0x05, // 10 consecutive values out of range
TSL2591_PERSIST_15 = 0x06, // 15 consecutive values out of range
TSL2591_PERSIST_20 = 0x07, // 20 consecutive values out of range
TSL2591_PERSIST_25 = 0x08, // 25 consecutive values out of range
TSL2591_PERSIST_30 = 0x09, // 30 consecutive values out of range
TSL2591_PERSIST_35 = 0x0A, // 35 consecutive values out of range
TSL2591_PERSIST_40 = 0x0B, // 40 consecutive values out of range
TSL2591_PERSIST_45 = 0x0C, // 45 consecutive values out of range
TSL2591_PERSIST_50 = 0x0D, // 50 consecutive values out of range
TSL2591_PERSIST_55 = 0x0E, // 55 consecutive values out of range
TSL2591_PERSIST_60 = 0x0F, // 60 consecutive values out of range
} tsl2591Persist_t;
/// Enumeration for the sensor gain
typedef enum {
TSL2591_GAIN_LOW = 0x00, /// low gain (1x)
TSL2591_GAIN_MED = 0x10, /// medium gain (25x)
TSL2591_GAIN_HIGH = 0x20, /// medium gain (428x)
TSL2591_GAIN_MAX = 0x30, /// max gain (9876x)
} tsl2591Gain_t;
enum ChipModel{
ChipModel_UNKNOWN = 0,
ChipModel_TSL25911_REV0 = 0x50
};
TSL25911(TwoWire* i2c): m_initialized(false), gain(TSL2591_GAIN_MED), integration(TSL2591_INTEGRATIONTIME_500MS),i2c(i2c){}
TSL25911(TwoWire* i2c, tsl2591Gain_t _gain,tsl2591IntegrationTime_t _integration) : m_initialized(false),gain(_gain),integration(_integration),i2c(i2c) {}
ChipModel chipModel();
bool begin();
bool reset();
uint16_t lumV();
uint16_t lumIR();
uint16_t lumF();
float lumLux();
void read(uint16_t& v, uint16_t& ir, uint16_t& f, float& lux);
protected:
bool Initialize();
private:
bool m_initialized;
ChipModel m_chip_model;
TwoWire* i2c;
tsl2591Gain_t gain;
tsl2591IntegrationTime_t integration;
static const uint8_t REG_ENABLE = 0xA0 | 0x00;
static const uint8_t REG_CTRL = 0xA0 | 0x01;
static const uint8_t PID_ADDR = 0xA0 | 0x11;
static const uint8_t ID_ADDR = 0xA0 | 0x12;
static const uint8_t DEVICE_STATUS = 0xA0 | 0x13; // Internal Status
static const uint8_t CHAN0_LOW = 0xA0 | 0x14; // Channel 0 data, low byte
static const uint8_t CHAN0_HIGH = 0xA0 | 0x15; // Channel 0 data, high byte
static const uint8_t CHAN1_LOW = 0xA0 | 0x16; // Channel 1 data, low byte
static const uint8_t CHAN1_HIGH = 0xA0 | 0x17; // Channel 1 data, high byte
float again();
float atime();
bool ReadChipID();
bool ReadRegister(uint8_t addr,uint8_t data[],uint8_t length);
bool ReadData(uint32_t data[SENSOR_DATA_LENGTH]);
void WriteSettings();
bool WriteRegister(uint8_t addr,uint8_t data);
};
#endif

129
src/sensor/environment.cpp Normal file
View File

@ -0,0 +1,129 @@
/*
EnvironmentCalculations.cpp
Copyright (C) 2016 Tyler Glenn
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Written: Dec 30 2015.
Last Updated: Dec 23 2017.
This header must be included in any derived code or copies of the code.
*/
#include "environment.h"
#include <Arduino.h>
#include <math.h>
#define hi_coeff1 -42.379
#define hi_coeff2 2.04901523
#define hi_coeff3 10.14333127
#define hi_coeff4 -0.22475541
#define hi_coeff5 -0.00683783
#define hi_coeff6 -0.05481717
#define hi_coeff7 0.00122874
#define hi_coeff8 0.00085282
#define hi_coeff9 -0.00000199
/****************************************************************/
float EnvironmentCalculations::Altitude(float pressure,float referencePressure,float outdoorTemp){
// Equation inverse to EquivalentSeaLevelPressure calculation.
float altitude = NAN;
if (!isnan(pressure) && !isnan(referencePressure) && !isnan(outdoorTemp)){
altitude = pow(referencePressure / pressure, 0.190234) - 1;
altitude *= ((outdoorTemp + 273.15) / 0.0065);
}
return altitude;
}
/****************************************************************/
float EnvironmentCalculations::AbsoluteHumidity(float temperature, float humidity){
//taken from https://carnotcycle.wordpress.com/2012/08/04/how-to-convert-relative-humidity-to-absolute-humidity/
//precision is about 0.1°C in range -30 to 35°C
//August-Roche-Magnus 6.1094 exp(17.625 x T)/(T + 243.04)
//Buck (1981) 6.1121 exp(17.502 x T)/(T + 240.97)
//reference https://www.eas.ualberta.ca/jdwilson/EAS372_13/Vomel_CIRES_satvpformulae.html
float temp = NAN;
const float mw = 18.01534; // molar mass of water g/mol
const float r = 8.31447215; // Universal gas constant J/mol/K
if (isnan(temperature) || isnan(humidity) ){
return NAN;
}
temp = pow(2.718281828, (17.67 * temperature) / (temperature + 243.5));
//return (6.112 * temp * humidity * 2.1674) / (273.15 + temperature); //simplified version
return (6.112 * temp * humidity * mw) / ((273.15 + temperature) * r); //long version
}
/****************************************************************/
//FYI: https://ehp.niehs.nih.gov/1206273/ in detail this flow graph: https://ehp.niehs.nih.gov/wp-content/uploads/2013/10/ehp.1206273.g003.png
float EnvironmentCalculations::HeatIndex(float temperature,float humidity){
float heatIndex(NAN);
if ( isnan(temperature) || isnan(humidity) ) {
return heatIndex;
}
temperature = (temperature * (9.0 / 5.0) + 32.0); /*conversion to [°F]*/
// Using both Rothfusz and Steadman's equations
// http://www.wpc.ncep.noaa.gov/html/heatindex_equation.shtml
if (temperature <= 40) {
heatIndex = temperature; //first red block
}else{
heatIndex = 0.5 * (temperature + 61.0 + ((temperature - 68.0) * 1.2) + (humidity * 0.094)); //calculate A -- from the official site, not the flow graph
if (heatIndex >= 79) {
/*
* calculate B
* the following calculation is optimized. Simply spoken, reduzed cpu-operations to minimize used ram and runtime.
* Check the correctness with the following link:
* http://www.wolframalpha.com/input/?source=nav&i=b%3D+x1+%2B+x2*T+%2B+x3*H+%2B+x4*T*H+%2B+x5*T*T+%2B+x6*H*H+%2B+x7*T*T*H+%2B+x8*T*H*H+%2B+x9*T*T*H*H
*/
heatIndex = hi_coeff1
+ (hi_coeff2 + hi_coeff4 * humidity + temperature * (hi_coeff5 + hi_coeff7 * humidity)) * temperature
+ (hi_coeff3 + humidity * (hi_coeff6 + temperature * (hi_coeff8 + hi_coeff9 * temperature))) * humidity;
//third red block
if ((humidity < 13) && (temperature >= 80.0) && (temperature <= 112.0)) {
heatIndex -= ((13.0 - humidity) * 0.25) * sqrt((17.0 - abs(temperature - 95.0)) * 0.05882);
}else if ((humidity > 85.0) && (temperature >= 80.0) && (temperature <= 87.0)){
heatIndex += (0.02 * (humidity - 85.0) * (87.0 - temperature));
}
}
}
return (heatIndex - 32.0) * (5.0 / 9.0); /*conversion back to [°C]*/
}
/****************************************************************/
float EnvironmentCalculations::EquivalentSeaLevelPressure(float altitude,float temp,float pres){
float seaPress = NAN;
if(!isnan(altitude) && !isnan(temp) && !isnan(pres)){
seaPress = (pres / pow(1 - ((0.0065 *altitude) / (temp + (0.0065 *altitude) + 273.15)), 5.257));
}
return seaPress;
}
/****************************************************************/
float EnvironmentCalculations::DewPoint(float temp,float hum){
// Equations courtesy of Brian McNoldy from http://andrew.rsmas.miami.edu;
float dewPoint = NAN;
if(!isnan(temp) && !isnan(hum)){
dewPoint = 243.04 * (log(hum/100.0) + ((17.625 * temp)/(243.04 + temp)))
/(17.625 - log(hum/100.0) - ((17.625 * temp)/(243.04 + temp)));
}
return dewPoint;
}

62
src/sensor/environment.h Normal file
View File

@ -0,0 +1,62 @@
#ifndef TG_ENVIRONMENT_CALCULATIONS_H
#define TG_ENVIRONMENT_CALCULATIONS_H
namespace EnvironmentCalculations{
/////////////////////////////////////////////////////////////////
/// Calculate the altitude based on the pressure and temperature
/// in temptUnit.
/// @param pressure at the station in any units.
/// @param altUnit meters or feet. default=AltitudeUnit_Meters
/// @param referencePressure (usually pressure on MSL)
/// in the same units as pressure. default=1013.25hPa (ISA)
/// @param outdoorTemp temperature at the station in tempUnit
/// default=15°C (ISA)
/// @return Calculated Altitude in meters.
float Altitude(
float pressure,
float referencePressure = 1013.25, // [hPa] ....ISA value
float outdoorTemp = 15 // [°C] .... ISA value
);
/////////////////////////////////////////////////////////////////
/// Calculate the heatindex based on the humidity and temperature
/// in tempUnit.
/// The formula based on the Heat Index Equation of the US National Weather Service
/// http://www.wpc.ncep.noaa.gov/html/heatindex_equation.shtml
/// @param temperature in tempUnit
/// @param humidity in percentage
/// @return Calculated heatindex as float in TempUnit
float HeatIndex(float temperature,float humidity);
/////////////////////////////////////////////////////////////////
/// Calculate the absolute humidity based on the relative humidity and temperature
/// in tempUnit.
/// the formula does work for values between -30°C and 35°C with 0.1°C precision
/// @param temperature in tempUnit
/// @param humidity in percentage
/// @return Calculated absolute humidity in grams/m³
float AbsoluteHumidity(float temperature, float humidity);
/////////////////////////////////////////////////////////////////
/// Convert current pressure to equivalent sea-level pressure.
/// @param altitude in meters.
/// @param temp in tempUnit.
/// @param pressure at the station in any units.
/// @return Equivalent pressure at sea level. The input pressure
/// unit will determine the output
/// pressure unit.
float EquivalentSeaLevelPressure(float altitude,float temp,float pres);
/////////////////////////////////////////////////////////////////
/// Calculate the dew point based on the temperature in tempUnit
/// and humidity.
/// @param temp in tempUnit.
/// @param hum in %.
float DewPoint(float temp,float hum);
}
#endif // TG_ENVIRONMENT_CALCULATIONS_H

41
src/sensor/sensor.cpp Normal file
View File

@ -0,0 +1,41 @@
#include "sensor.h"
#include "environment.h"
Sensor::Sensor(TwoWire* i2c){
bme = new BME280(i2c);
ltr = new LTR390(i2c);
tsl = new TSL25911(i2c);
sgp = new SGP40(i2c);
pms = new PMSA003(i2c);
}
void Sensor::TSL_init(void){tsl->begin();}
void Sensor::BME_init(void){bme->begin();}
void Sensor::ICM_init(void){}//Nothing to do afaik
void Sensor::LTR_init(void){ltr->begin();}
void Sensor::SGP_init(void){sgp->begin();}
void Sensor::PMS_init(void){pms->begin();}
void Sensor::BME_measure(){
bme->read(pres, temp, hum);
heatidx = EnvironmentCalculations::HeatIndex(temp,hum);
}
void Sensor::LTR_measure(){
ltr->read(uv, uvi);
}
void Sensor::TSL_measure(void){
tsl->read(light_vis, light_ir, light_full, light_lux);
}
void Sensor::ICM_measure(void){
//Do realy care about this ????
}
void Sensor::SGP_measure(void){
sgp->read(voci, temp, hum);
}
void Sensor::PMS_measure(void){
pms->read(pmd);
}

71
src/sensor/sensor.h Normal file
View File

@ -0,0 +1,71 @@
#ifndef SENSOR_H
#define SENSOR_H
#include <Wire.h>
#include "environment.h"
#include "dev/bme280.h"
#include "dev/ltr390.h"
#include "dev/icm20948.h"
#include "dev/sgp40.h"
#include "dev/tsl25911.h"
#include "dev/pmsa003.h"
class Sensor
{
// user-accessible "public" interface
public:
Sensor(TwoWire* i2c);
float temp = 0;
float pres = 0;
float hum = 0;
float heatidx = 0;
float light = 0;
uint32_t uv = 0;
uint32_t uvi = 0;
uint16_t light_full = 0;
uint16_t light_vis = 0;
uint16_t light_ir = 0;
float light_lux = 0;
int32_t voci = 0;
PM25_AQI_Data pmd = {0};
void TSL_init(void); //Light Sensor
void BME_init(void); //Temp, Hum, Presssure
void ICM_init(void); //motion,accel, gyro, magnet
void LTR_init(void); //UV, AL
void SGP_init(void); //VOC
void PMS_init(void); //PM2.5 - PM10
void TSL_measure(void);
void BME_measure(void);
void ICM_measure(void);
void LTR_measure(void);
void SGP_measure(void);
void PMS_measure(void);
void measure(){
TSL_measure();
BME_measure();
ICM_measure();
LTR_measure();
SGP_measure();
PMS_measure();
}
private:
BME280* bme = NULL;
LTR390* ltr = NULL;
TSL25911* tsl = NULL;
SGP40* sgp = NULL;
PMSA003* pms = NULL;
};
#endif

804
src/sensor/voc.cpp Normal file
View File

@ -0,0 +1,804 @@
/*
* Copyright (c) 2020, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "voc.h"
/* The fixed point arithmetic parts of this code were originally created by
* https://github.com/PetteriAimonen/libfixmath
*/
/*!< the maximum value of fix16_t */
#define FIX16_MAXIMUM 0x7FFFFFFF
/*!< the minimum value of fix16_t */
#define FIX16_MINIMUM 0x80000000
/*!< the value used to indicate overflows when FIXMATH_NO_OVERFLOW is not
* specified */
#define FIX16_OVERFLOW 0x80000000
/*!< fix16_t value of 1 */
#define FIX16_ONE 0x00010000
inline fix16_t fix16_from_int(int32_t a) {
return a * FIX16_ONE;
}
inline int32_t fix16_cast_to_int(fix16_t a) {
return (a >> 16);
}
/*! Multiplies the two given fix16_t's and returns the result. */
static fix16_t fix16_mul(fix16_t inArg0, fix16_t inArg1);
/*! Divides the first given fix16_t by the second and returns the result. */
static fix16_t fix16_div(fix16_t inArg0, fix16_t inArg1);
/*! Returns the square root of the given fix16_t. */
static fix16_t fix16_sqrt(fix16_t inValue);
/*! Returns the exponent (e^) of the given fix16_t. */
static fix16_t fix16_exp(fix16_t inValue);
static fix16_t fix16_mul(fix16_t inArg0, fix16_t inArg1) {
// Each argument is divided to 16-bit parts.
// AB
// * CD
// -----------
// BD 16 * 16 -> 32 bit products
// CB
// AD
// AC
// |----| 64 bit product
int32_t A = (inArg0 >> 16), C = (inArg1 >> 16);
uint32_t B = (inArg0 & 0xFFFF), D = (inArg1 & 0xFFFF);
int32_t AC = A * C;
int32_t AD_CB = A * D + C * B;
uint32_t BD = B * D;
int32_t product_hi = AC + (AD_CB >> 16);
// Handle carry from lower 32 bits to upper part of result.
uint32_t ad_cb_temp = AD_CB << 16;
uint32_t product_lo = BD + ad_cb_temp;
if (product_lo < BD)
product_hi++;
#ifndef FIXMATH_NO_OVERFLOW
// The upper 17 bits should all be the same (the sign).
if (product_hi >> 31 != product_hi >> 15)
return FIX16_OVERFLOW;
#endif
#ifdef FIXMATH_NO_ROUNDING
return (product_hi << 16) | (product_lo >> 16);
#else
// Subtracting 0x8000 (= 0.5) and then using signed right shift
// achieves proper rounding to result-1, except in the corner
// case of negative numbers and lowest word = 0x8000.
// To handle that, we also have to subtract 1 for negative numbers.
uint32_t product_lo_tmp = product_lo;
product_lo -= 0x8000;
product_lo -= (uint32_t)product_hi >> 31;
if (product_lo > product_lo_tmp)
product_hi--;
// Discard the lowest 16 bits. Note that this is not exactly the same
// as dividing by 0x10000. For example if product = -1, result will
// also be -1 and not 0. This is compensated by adding +1 to the result
// and compensating this in turn in the rounding above.
fix16_t result = (product_hi << 16) | (product_lo >> 16);
result += 1;
return result;
#endif
}
static fix16_t fix16_div(fix16_t a, fix16_t b) {
// This uses the basic binary restoring division algorithm.
// It appears to be faster to do the whole division manually than
// trying to compose a 64-bit divide out of 32-bit divisions on
// platforms without hardware divide.
if (b == 0)
return FIX16_MINIMUM;
uint32_t remainder = (a >= 0) ? a : (-a);
uint32_t divider = (b >= 0) ? b : (-b);
uint32_t quotient = 0;
uint32_t bit = 0x10000;
/* The algorithm requires D >= R */
while (divider < remainder) {
divider <<= 1;
bit <<= 1;
}
#ifndef FIXMATH_NO_OVERFLOW
if (!bit)
return FIX16_OVERFLOW;
#endif
if (divider & 0x80000000) {
// Perform one step manually to avoid overflows later.
// We know that divider's bottom bit is 0 here.
if (remainder >= divider) {
quotient |= bit;
remainder -= divider;
}
divider >>= 1;
bit >>= 1;
}
/* Main division loop */
while (bit && remainder) {
if (remainder >= divider) {
quotient |= bit;
remainder -= divider;
}
remainder <<= 1;
bit >>= 1;
}
#ifndef FIXMATH_NO_ROUNDING
if (remainder >= divider) {
quotient++;
}
#endif
fix16_t result = quotient;
/* Figure out the sign of result */
if ((a ^ b) & 0x80000000) {
#ifndef FIXMATH_NO_OVERFLOW
if (result == FIX16_MINIMUM)
return FIX16_OVERFLOW;
#endif
result = -result;
}
return result;
}
static fix16_t fix16_sqrt(fix16_t x) {
// It is assumed that x is not negative
uint32_t num = x;
uint32_t result = 0;
uint32_t bit;
uint8_t n;
bit = (uint32_t)1 << 30;
while (bit > num)
bit >>= 2;
// The main part is executed twice, in order to avoid
// using 64 bit values in computations.
for (n = 0; n < 2; n++) {
// First we get the top 24 bits of the answer.
while (bit) {
if (num >= result + bit) {
num -= result + bit;
result = (result >> 1) + bit;
} else {
result = (result >> 1);
}
bit >>= 2;
}
if (n == 0) {
// Then process it again to get the lowest 8 bits.
if (num > 65535) {
// The remainder 'num' is too large to be shifted left
// by 16, so we have to add 1 to result manually and
// adjust 'num' accordingly.
// num = a - (result + 0.5)^2
// = num + result^2 - (result + 0.5)^2
// = num - result - 0.5
num -= result;
num = (num << 16) - 0x8000;
result = (result << 16) + 0x8000;
} else {
num <<= 16;
result <<= 16;
}
bit = 1 << 14;
}
}
#ifndef FIXMATH_NO_ROUNDING
// Finally, if next bit would have been 1, round the result upwards.
if (num > result) {
result++;
}
#endif
return (fix16_t)result;
}
static fix16_t fix16_exp(fix16_t x) {
// Function to approximate exp(); optimized more for code size than speed
// exp(x) for x = +/- {1, 1/8, 1/64, 1/512}
#define NUM_EXP_VALUES 4
static const fix16_t exp_pos_values[NUM_EXP_VALUES] = {
F16(2.7182818), F16(1.1331485), F16(1.0157477), F16(1.0019550)};
static const fix16_t exp_neg_values[NUM_EXP_VALUES] = {
F16(0.3678794), F16(0.8824969), F16(0.9844964), F16(0.9980488)};
const fix16_t* exp_values;
fix16_t res, arg;
uint16_t i;
if (x >= F16(10.3972))
return FIX16_MAXIMUM;
if (x <= F16(-11.7835))
return 0;
if (x < 0) {
x = -x;
exp_values = exp_neg_values;
} else {
exp_values = exp_pos_values;
}
res = FIX16_ONE;
arg = FIX16_ONE;
for (i = 0; i < NUM_EXP_VALUES; i++) {
while (x >= arg) {
res = fix16_mul(res, exp_values[i]);
x -= arg;
}
arg >>= 3;
}
return res;
}
static void VocAlgorithm__init_instances(VocAlgorithmParams* params);
static void
VocAlgorithm__mean_variance_estimator__init(VocAlgorithmParams* params);
static void VocAlgorithm__mean_variance_estimator___init_instances(
VocAlgorithmParams* params);
static void VocAlgorithm__mean_variance_estimator__set_parameters(
VocAlgorithmParams* params, fix16_t std_initial,
fix16_t tau_mean_variance_hours, fix16_t gating_max_duration_minutes);
static void
VocAlgorithm__mean_variance_estimator__set_states(VocAlgorithmParams* params,
fix16_t mean, fix16_t std,
fix16_t uptime_gamma);
static fix16_t
VocAlgorithm__mean_variance_estimator__get_std(VocAlgorithmParams* params);
static fix16_t
VocAlgorithm__mean_variance_estimator__get_mean(VocAlgorithmParams* params);
static void VocAlgorithm__mean_variance_estimator___calculate_gamma(
VocAlgorithmParams* params, fix16_t voc_index_from_prior);
static void VocAlgorithm__mean_variance_estimator__process(
VocAlgorithmParams* params, fix16_t sraw, fix16_t voc_index_from_prior);
static void VocAlgorithm__mean_variance_estimator___sigmoid__init(
VocAlgorithmParams* params);
static void VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
VocAlgorithmParams* params, fix16_t L, fix16_t X0, fix16_t K);
static fix16_t VocAlgorithm__mean_variance_estimator___sigmoid__process(
VocAlgorithmParams* params, fix16_t sample);
static void VocAlgorithm__mox_model__init(VocAlgorithmParams* params);
static void VocAlgorithm__mox_model__set_parameters(VocAlgorithmParams* params,
fix16_t SRAW_STD,
fix16_t SRAW_MEAN);
static fix16_t VocAlgorithm__mox_model__process(VocAlgorithmParams* params,
fix16_t sraw);
static void VocAlgorithm__sigmoid_scaled__init(VocAlgorithmParams* params);
static void
VocAlgorithm__sigmoid_scaled__set_parameters(VocAlgorithmParams* params,
fix16_t offset);
static fix16_t VocAlgorithm__sigmoid_scaled__process(VocAlgorithmParams* params,
fix16_t sample);
static void VocAlgorithm__adaptive_lowpass__init(VocAlgorithmParams* params);
static void
VocAlgorithm__adaptive_lowpass__set_parameters(VocAlgorithmParams* params);
static fix16_t
VocAlgorithm__adaptive_lowpass__process(VocAlgorithmParams* params,
fix16_t sample);
void VocAlgorithm_init(VocAlgorithmParams* params) {
params->mVoc_Index_Offset = F16(VocAlgorithm_VOC_INDEX_OFFSET_DEFAULT);
params->mTau_Mean_Variance_Hours =
F16(VocAlgorithm_TAU_MEAN_VARIANCE_HOURS);
params->mGating_Max_Duration_Minutes =
F16(VocAlgorithm_GATING_MAX_DURATION_MINUTES);
params->mSraw_Std_Initial = F16(VocAlgorithm_SRAW_STD_INITIAL);
params->mUptime = F16(0.);
params->mSraw = F16(0.);
params->mVoc_Index = 0;
VocAlgorithm__init_instances(params);
}
static void VocAlgorithm__init_instances(VocAlgorithmParams* params) {
VocAlgorithm__mean_variance_estimator__init(params);
VocAlgorithm__mean_variance_estimator__set_parameters(
params, params->mSraw_Std_Initial, params->mTau_Mean_Variance_Hours,
params->mGating_Max_Duration_Minutes);
VocAlgorithm__mox_model__init(params);
VocAlgorithm__mox_model__set_parameters(
params, VocAlgorithm__mean_variance_estimator__get_std(params),
VocAlgorithm__mean_variance_estimator__get_mean(params));
VocAlgorithm__sigmoid_scaled__init(params);
VocAlgorithm__sigmoid_scaled__set_parameters(params,
params->mVoc_Index_Offset);
VocAlgorithm__adaptive_lowpass__init(params);
VocAlgorithm__adaptive_lowpass__set_parameters(params);
}
void VocAlgorithm_get_states(VocAlgorithmParams* params, int32_t* state0,
int32_t* state1) {
*state0 = VocAlgorithm__mean_variance_estimator__get_mean(params);
*state1 = VocAlgorithm__mean_variance_estimator__get_std(params);
return;
}
void VocAlgorithm_set_states(VocAlgorithmParams* params, int32_t state0,
int32_t state1) {
VocAlgorithm__mean_variance_estimator__set_states(
params, state0, state1, F16(VocAlgorithm_PERSISTENCE_UPTIME_GAMMA));
params->mSraw = state0;
}
void VocAlgorithm_set_tuning_parameters(VocAlgorithmParams* params,
int32_t voc_index_offset,
int32_t learning_time_hours,
int32_t gating_max_duration_minutes,
int32_t std_initial) {
params->mVoc_Index_Offset = (fix16_from_int(voc_index_offset));
params->mTau_Mean_Variance_Hours = (fix16_from_int(learning_time_hours));
params->mGating_Max_Duration_Minutes =
(fix16_from_int(gating_max_duration_minutes));
params->mSraw_Std_Initial = (fix16_from_int(std_initial));
VocAlgorithm__init_instances(params);
}
void VocAlgorithm_process(VocAlgorithmParams* params, int32_t sraw,
int32_t* voc_index) {
if ((params->mUptime <= F16(VocAlgorithm_INITIAL_BLACKOUT))) {
params->mUptime =
(params->mUptime + F16(VocAlgorithm_SAMPLING_INTERVAL));
} else {
if (((sraw > 0) && (sraw < 65000))) {
if ((sraw < 20001)) {
sraw = 20001;
} else if ((sraw > 52767)) {
sraw = 52767;
}
params->mSraw = (fix16_from_int((sraw - 20000)));
}
params->mVoc_Index =
VocAlgorithm__mox_model__process(params, params->mSraw);
params->mVoc_Index =
VocAlgorithm__sigmoid_scaled__process(params, params->mVoc_Index);
params->mVoc_Index =
VocAlgorithm__adaptive_lowpass__process(params, params->mVoc_Index);
if ((params->mVoc_Index < F16(0.5))) {
params->mVoc_Index = F16(0.5);
}
if ((params->mSraw > F16(0.))) {
VocAlgorithm__mean_variance_estimator__process(
params, params->mSraw, params->mVoc_Index);
VocAlgorithm__mox_model__set_parameters(
params, VocAlgorithm__mean_variance_estimator__get_std(params),
VocAlgorithm__mean_variance_estimator__get_mean(params));
}
}
*voc_index = (fix16_cast_to_int((params->mVoc_Index + F16(0.5))));
return;
}
static void
VocAlgorithm__mean_variance_estimator__init(VocAlgorithmParams* params) {
VocAlgorithm__mean_variance_estimator__set_parameters(params, F16(0.),
F16(0.), F16(0.));
VocAlgorithm__mean_variance_estimator___init_instances(params);
}
static void VocAlgorithm__mean_variance_estimator___init_instances(
VocAlgorithmParams* params) {
VocAlgorithm__mean_variance_estimator___sigmoid__init(params);
}
static void VocAlgorithm__mean_variance_estimator__set_parameters(
VocAlgorithmParams* params, fix16_t std_initial,
fix16_t tau_mean_variance_hours, fix16_t gating_max_duration_minutes) {
params->m_Mean_Variance_Estimator__Gating_Max_Duration_Minutes =
gating_max_duration_minutes;
params->m_Mean_Variance_Estimator___Initialized = false;
params->m_Mean_Variance_Estimator___Mean = F16(0.);
params->m_Mean_Variance_Estimator___Sraw_Offset = F16(0.);
params->m_Mean_Variance_Estimator___Std = std_initial;
params->m_Mean_Variance_Estimator___Gamma =
(fix16_div(F16((VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING *
(VocAlgorithm_SAMPLING_INTERVAL / 3600.))),
(tau_mean_variance_hours +
F16((VocAlgorithm_SAMPLING_INTERVAL / 3600.)))));
params->m_Mean_Variance_Estimator___Gamma_Initial_Mean =
F16(((VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING *
VocAlgorithm_SAMPLING_INTERVAL) /
(VocAlgorithm_TAU_INITIAL_MEAN + VocAlgorithm_SAMPLING_INTERVAL)));
params->m_Mean_Variance_Estimator___Gamma_Initial_Variance = F16(
((VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING *
VocAlgorithm_SAMPLING_INTERVAL) /
(VocAlgorithm_TAU_INITIAL_VARIANCE + VocAlgorithm_SAMPLING_INTERVAL)));
params->m_Mean_Variance_Estimator__Gamma_Mean = F16(0.);
params->m_Mean_Variance_Estimator__Gamma_Variance = F16(0.);
params->m_Mean_Variance_Estimator___Uptime_Gamma = F16(0.);
params->m_Mean_Variance_Estimator___Uptime_Gating = F16(0.);
params->m_Mean_Variance_Estimator___Gating_Duration_Minutes = F16(0.);
}
static void
VocAlgorithm__mean_variance_estimator__set_states(VocAlgorithmParams* params,
fix16_t mean, fix16_t std,
fix16_t uptime_gamma) {
params->m_Mean_Variance_Estimator___Mean = mean;
params->m_Mean_Variance_Estimator___Std = std;
params->m_Mean_Variance_Estimator___Uptime_Gamma = uptime_gamma;
params->m_Mean_Variance_Estimator___Initialized = true;
}
static fix16_t
VocAlgorithm__mean_variance_estimator__get_std(VocAlgorithmParams* params) {
return params->m_Mean_Variance_Estimator___Std;
}
static fix16_t
VocAlgorithm__mean_variance_estimator__get_mean(VocAlgorithmParams* params) {
return (params->m_Mean_Variance_Estimator___Mean +
params->m_Mean_Variance_Estimator___Sraw_Offset);
}
static void VocAlgorithm__mean_variance_estimator___calculate_gamma(
VocAlgorithmParams* params, fix16_t voc_index_from_prior) {
fix16_t uptime_limit;
fix16_t sigmoid_gamma_mean;
fix16_t gamma_mean;
fix16_t gating_threshold_mean;
fix16_t sigmoid_gating_mean;
fix16_t sigmoid_gamma_variance;
fix16_t gamma_variance;
fix16_t gating_threshold_variance;
fix16_t sigmoid_gating_variance;
uptime_limit = F16((VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__FIX16_MAX -
VocAlgorithm_SAMPLING_INTERVAL));
if ((params->m_Mean_Variance_Estimator___Uptime_Gamma < uptime_limit)) {
params->m_Mean_Variance_Estimator___Uptime_Gamma =
(params->m_Mean_Variance_Estimator___Uptime_Gamma +
F16(VocAlgorithm_SAMPLING_INTERVAL));
}
if ((params->m_Mean_Variance_Estimator___Uptime_Gating < uptime_limit)) {
params->m_Mean_Variance_Estimator___Uptime_Gating =
(params->m_Mean_Variance_Estimator___Uptime_Gating +
F16(VocAlgorithm_SAMPLING_INTERVAL));
}
VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
params, F16(1.), F16(VocAlgorithm_INIT_DURATION_MEAN),
F16(VocAlgorithm_INIT_TRANSITION_MEAN));
sigmoid_gamma_mean =
VocAlgorithm__mean_variance_estimator___sigmoid__process(
params, params->m_Mean_Variance_Estimator___Uptime_Gamma);
gamma_mean =
(params->m_Mean_Variance_Estimator___Gamma +
(fix16_mul((params->m_Mean_Variance_Estimator___Gamma_Initial_Mean -
params->m_Mean_Variance_Estimator___Gamma),
sigmoid_gamma_mean)));
gating_threshold_mean =
(F16(VocAlgorithm_GATING_THRESHOLD) +
(fix16_mul(
F16((VocAlgorithm_GATING_THRESHOLD_INITIAL -
VocAlgorithm_GATING_THRESHOLD)),
VocAlgorithm__mean_variance_estimator___sigmoid__process(
params, params->m_Mean_Variance_Estimator___Uptime_Gating))));
VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
params, F16(1.), gating_threshold_mean,
F16(VocAlgorithm_GATING_THRESHOLD_TRANSITION));
sigmoid_gating_mean =
VocAlgorithm__mean_variance_estimator___sigmoid__process(
params, voc_index_from_prior);
params->m_Mean_Variance_Estimator__Gamma_Mean =
(fix16_mul(sigmoid_gating_mean, gamma_mean));
VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
params, F16(1.), F16(VocAlgorithm_INIT_DURATION_VARIANCE),
F16(VocAlgorithm_INIT_TRANSITION_VARIANCE));
sigmoid_gamma_variance =
VocAlgorithm__mean_variance_estimator___sigmoid__process(
params, params->m_Mean_Variance_Estimator___Uptime_Gamma);
gamma_variance =
(params->m_Mean_Variance_Estimator___Gamma +
(fix16_mul(
(params->m_Mean_Variance_Estimator___Gamma_Initial_Variance -
params->m_Mean_Variance_Estimator___Gamma),
(sigmoid_gamma_variance - sigmoid_gamma_mean))));
gating_threshold_variance =
(F16(VocAlgorithm_GATING_THRESHOLD) +
(fix16_mul(
F16((VocAlgorithm_GATING_THRESHOLD_INITIAL -
VocAlgorithm_GATING_THRESHOLD)),
VocAlgorithm__mean_variance_estimator___sigmoid__process(
params, params->m_Mean_Variance_Estimator___Uptime_Gating))));
VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
params, F16(1.), gating_threshold_variance,
F16(VocAlgorithm_GATING_THRESHOLD_TRANSITION));
sigmoid_gating_variance =
VocAlgorithm__mean_variance_estimator___sigmoid__process(
params, voc_index_from_prior);
params->m_Mean_Variance_Estimator__Gamma_Variance =
(fix16_mul(sigmoid_gating_variance, gamma_variance));
params->m_Mean_Variance_Estimator___Gating_Duration_Minutes =
(params->m_Mean_Variance_Estimator___Gating_Duration_Minutes +
(fix16_mul(F16((VocAlgorithm_SAMPLING_INTERVAL / 60.)),
((fix16_mul((F16(1.) - sigmoid_gating_mean),
F16((1. + VocAlgorithm_GATING_MAX_RATIO)))) -
F16(VocAlgorithm_GATING_MAX_RATIO)))));
if ((params->m_Mean_Variance_Estimator___Gating_Duration_Minutes <
F16(0.))) {
params->m_Mean_Variance_Estimator___Gating_Duration_Minutes = F16(0.);
}
if ((params->m_Mean_Variance_Estimator___Gating_Duration_Minutes >
params->m_Mean_Variance_Estimator__Gating_Max_Duration_Minutes)) {
params->m_Mean_Variance_Estimator___Uptime_Gating = F16(0.);
}
}
static void VocAlgorithm__mean_variance_estimator__process(
VocAlgorithmParams* params, fix16_t sraw, fix16_t voc_index_from_prior) {
fix16_t delta_sgp;
fix16_t c;
fix16_t additional_scaling;
if ((params->m_Mean_Variance_Estimator___Initialized == false)) {
params->m_Mean_Variance_Estimator___Initialized = true;
params->m_Mean_Variance_Estimator___Sraw_Offset = sraw;
params->m_Mean_Variance_Estimator___Mean = F16(0.);
} else {
if (((params->m_Mean_Variance_Estimator___Mean >= F16(100.)) ||
(params->m_Mean_Variance_Estimator___Mean <= F16(-100.)))) {
params->m_Mean_Variance_Estimator___Sraw_Offset =
(params->m_Mean_Variance_Estimator___Sraw_Offset +
params->m_Mean_Variance_Estimator___Mean);
params->m_Mean_Variance_Estimator___Mean = F16(0.);
}
sraw = (sraw - params->m_Mean_Variance_Estimator___Sraw_Offset);
VocAlgorithm__mean_variance_estimator___calculate_gamma(
params, voc_index_from_prior);
delta_sgp = (fix16_div(
(sraw - params->m_Mean_Variance_Estimator___Mean),
F16(VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING)));
if ((delta_sgp < F16(0.))) {
c = (params->m_Mean_Variance_Estimator___Std - delta_sgp);
} else {
c = (params->m_Mean_Variance_Estimator___Std + delta_sgp);
}
additional_scaling = F16(1.);
if ((c > F16(1440.))) {
additional_scaling = F16(4.);
}
params->m_Mean_Variance_Estimator___Std = (fix16_mul(
fix16_sqrt((fix16_mul(
additional_scaling,
(F16(VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING) -
params->m_Mean_Variance_Estimator__Gamma_Variance)))),
fix16_sqrt((
(fix16_mul(
params->m_Mean_Variance_Estimator___Std,
(fix16_div(
params->m_Mean_Variance_Estimator___Std,
(fix16_mul(
F16(VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING),
additional_scaling)))))) +
(fix16_mul(
(fix16_div(
(fix16_mul(
params->m_Mean_Variance_Estimator__Gamma_Variance,
delta_sgp)),
additional_scaling)),
delta_sgp))))));
params->m_Mean_Variance_Estimator___Mean =
(params->m_Mean_Variance_Estimator___Mean +
(fix16_mul(params->m_Mean_Variance_Estimator__Gamma_Mean,
delta_sgp)));
}
}
static void VocAlgorithm__mean_variance_estimator___sigmoid__init(
VocAlgorithmParams* params) {
VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
params, F16(0.), F16(0.), F16(0.));
}
static void VocAlgorithm__mean_variance_estimator___sigmoid__set_parameters(
VocAlgorithmParams* params, fix16_t L, fix16_t X0, fix16_t K) {
params->m_Mean_Variance_Estimator___Sigmoid__L = L;
params->m_Mean_Variance_Estimator___Sigmoid__K = K;
params->m_Mean_Variance_Estimator___Sigmoid__X0 = X0;
}
static fix16_t VocAlgorithm__mean_variance_estimator___sigmoid__process(
VocAlgorithmParams* params, fix16_t sample) {
fix16_t x;
x = (fix16_mul(params->m_Mean_Variance_Estimator___Sigmoid__K,
(sample - params->m_Mean_Variance_Estimator___Sigmoid__X0)));
if ((x < F16(-50.))) {
return params->m_Mean_Variance_Estimator___Sigmoid__L;
} else if ((x > F16(50.))) {
return F16(0.);
} else {
return (fix16_div(params->m_Mean_Variance_Estimator___Sigmoid__L,
(F16(1.) + fix16_exp(x))));
}
}
static void VocAlgorithm__mox_model__init(VocAlgorithmParams* params) {
VocAlgorithm__mox_model__set_parameters(params, F16(1.), F16(0.));
}
static void VocAlgorithm__mox_model__set_parameters(VocAlgorithmParams* params,
fix16_t SRAW_STD,
fix16_t SRAW_MEAN) {
params->m_Mox_Model__Sraw_Std = SRAW_STD;
params->m_Mox_Model__Sraw_Mean = SRAW_MEAN;
}
static fix16_t VocAlgorithm__mox_model__process(VocAlgorithmParams* params,
fix16_t sraw) {
return (fix16_mul((fix16_div((sraw - params->m_Mox_Model__Sraw_Mean),
(-(params->m_Mox_Model__Sraw_Std +
F16(VocAlgorithm_SRAW_STD_BONUS))))),
F16(VocAlgorithm_VOC_INDEX_GAIN)));
}
static void VocAlgorithm__sigmoid_scaled__init(VocAlgorithmParams* params) {
VocAlgorithm__sigmoid_scaled__set_parameters(params, F16(0.));
}
static void
VocAlgorithm__sigmoid_scaled__set_parameters(VocAlgorithmParams* params,
fix16_t offset) {
params->m_Sigmoid_Scaled__Offset = offset;
}
static fix16_t VocAlgorithm__sigmoid_scaled__process(VocAlgorithmParams* params,
fix16_t sample) {
fix16_t x;
fix16_t shift;
x = (fix16_mul(F16(VocAlgorithm_SIGMOID_K),
(sample - F16(VocAlgorithm_SIGMOID_X0))));
if ((x < F16(-50.))) {
return F16(VocAlgorithm_SIGMOID_L);
} else if ((x > F16(50.))) {
return F16(0.);
} else {
if ((sample >= F16(0.))) {
shift = (fix16_div(
(F16(VocAlgorithm_SIGMOID_L) -
(fix16_mul(F16(5.), params->m_Sigmoid_Scaled__Offset))),
F16(4.)));
return ((fix16_div((F16(VocAlgorithm_SIGMOID_L) + shift),
(F16(1.) + fix16_exp(x)))) -
shift);
} else {
return (fix16_mul(
(fix16_div(params->m_Sigmoid_Scaled__Offset,
F16(VocAlgorithm_VOC_INDEX_OFFSET_DEFAULT))),
(fix16_div(F16(VocAlgorithm_SIGMOID_L),
(F16(1.) + fix16_exp(x))))));
}
}
}
static void VocAlgorithm__adaptive_lowpass__init(VocAlgorithmParams* params) {
VocAlgorithm__adaptive_lowpass__set_parameters(params);
}
static void
VocAlgorithm__adaptive_lowpass__set_parameters(VocAlgorithmParams* params) {
params->m_Adaptive_Lowpass__A1 =
F16((VocAlgorithm_SAMPLING_INTERVAL /
(VocAlgorithm_LP_TAU_FAST + VocAlgorithm_SAMPLING_INTERVAL)));
params->m_Adaptive_Lowpass__A2 =
F16((VocAlgorithm_SAMPLING_INTERVAL /
(VocAlgorithm_LP_TAU_SLOW + VocAlgorithm_SAMPLING_INTERVAL)));
params->m_Adaptive_Lowpass___Initialized = false;
}
static fix16_t
VocAlgorithm__adaptive_lowpass__process(VocAlgorithmParams* params,
fix16_t sample) {
fix16_t abs_delta;
fix16_t F1;
fix16_t tau_a;
fix16_t a3;
if ((params->m_Adaptive_Lowpass___Initialized == false)) {
params->m_Adaptive_Lowpass___X1 = sample;
params->m_Adaptive_Lowpass___X2 = sample;
params->m_Adaptive_Lowpass___X3 = sample;
params->m_Adaptive_Lowpass___Initialized = true;
}
params->m_Adaptive_Lowpass___X1 =
((fix16_mul((F16(1.) - params->m_Adaptive_Lowpass__A1),
params->m_Adaptive_Lowpass___X1)) +
(fix16_mul(params->m_Adaptive_Lowpass__A1, sample)));
params->m_Adaptive_Lowpass___X2 =
((fix16_mul((F16(1.) - params->m_Adaptive_Lowpass__A2),
params->m_Adaptive_Lowpass___X2)) +
(fix16_mul(params->m_Adaptive_Lowpass__A2, sample)));
abs_delta =
(params->m_Adaptive_Lowpass___X1 - params->m_Adaptive_Lowpass___X2);
if ((abs_delta < F16(0.))) {
abs_delta = (-abs_delta);
}
F1 = fix16_exp((fix16_mul(F16(VocAlgorithm_LP_ALPHA), abs_delta)));
tau_a =
((fix16_mul(F16((VocAlgorithm_LP_TAU_SLOW - VocAlgorithm_LP_TAU_FAST)),
F1)) +
F16(VocAlgorithm_LP_TAU_FAST));
a3 = (fix16_div(F16(VocAlgorithm_SAMPLING_INTERVAL),
(F16(VocAlgorithm_SAMPLING_INTERVAL) + tau_a)));
params->m_Adaptive_Lowpass___X3 =
((fix16_mul((F16(1.) - a3), params->m_Adaptive_Lowpass___X3)) +
(fix16_mul(a3, sample)));
return params->m_Adaptive_Lowpass___X3;
}

185
src/sensor/voc.h Normal file
View File

@ -0,0 +1,185 @@
/*
* Copyright (c) 2020, Sensirion AG
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Sensirion AG nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef VOCALGORITHM_H_
#define VOCALGORITHM_H_
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#define SENSIRION_I2C_CLOCK_PERIOD_USEC 10
/* The fixed point arithmetic parts of this code were originally created by
* https://github.com/PetteriAimonen/libfixmath
*/
typedef int32_t fix16_t;
#define F16(x) \
((fix16_t)(((x) >= 0) ? ((x)*65536.0 + 0.5) : ((x)*65536.0 - 0.5)))
#define VocAlgorithm_SAMPLING_INTERVAL (1.)
#define VocAlgorithm_INITIAL_BLACKOUT (45.)
#define VocAlgorithm_VOC_INDEX_GAIN (230.)
#define VocAlgorithm_SRAW_STD_INITIAL (50.)
#define VocAlgorithm_SRAW_STD_BONUS (220.)
#define VocAlgorithm_TAU_MEAN_VARIANCE_HOURS (12.)
#define VocAlgorithm_TAU_INITIAL_MEAN (20.)
#define VocAlgorithm_INIT_DURATION_MEAN ((3600. * 0.75))
#define VocAlgorithm_INIT_TRANSITION_MEAN (0.01)
#define VocAlgorithm_TAU_INITIAL_VARIANCE (2500.)
#define VocAlgorithm_INIT_DURATION_VARIANCE ((3600. * 1.45))
#define VocAlgorithm_INIT_TRANSITION_VARIANCE (0.01)
#define VocAlgorithm_GATING_THRESHOLD (340.)
#define VocAlgorithm_GATING_THRESHOLD_INITIAL (510.)
#define VocAlgorithm_GATING_THRESHOLD_TRANSITION (0.09)
#define VocAlgorithm_GATING_MAX_DURATION_MINUTES ((60. * 3.))
#define VocAlgorithm_GATING_MAX_RATIO (0.3)
#define VocAlgorithm_SIGMOID_L (500.)
#define VocAlgorithm_SIGMOID_K (-0.0065)
#define VocAlgorithm_SIGMOID_X0 (213.)
#define VocAlgorithm_VOC_INDEX_OFFSET_DEFAULT (100.)
#define VocAlgorithm_LP_TAU_FAST (20.0)
#define VocAlgorithm_LP_TAU_SLOW (500.0)
#define VocAlgorithm_LP_ALPHA (-0.2)
#define VocAlgorithm_PERSISTENCE_UPTIME_GAMMA ((3. * 3600.))
#define VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__GAMMA_SCALING (64.)
#define VocAlgorithm_MEAN_VARIANCE_ESTIMATOR__FIX16_MAX (32767.)
/**
* Struct to hold all the states of the VOC algorithm.
*/
typedef struct {
fix16_t mVoc_Index_Offset;
fix16_t mTau_Mean_Variance_Hours;
fix16_t mGating_Max_Duration_Minutes;
fix16_t mSraw_Std_Initial;
fix16_t mUptime;
fix16_t mSraw;
fix16_t mVoc_Index;
fix16_t m_Mean_Variance_Estimator__Gating_Max_Duration_Minutes;
bool m_Mean_Variance_Estimator___Initialized;
fix16_t m_Mean_Variance_Estimator___Mean;
fix16_t m_Mean_Variance_Estimator___Sraw_Offset;
fix16_t m_Mean_Variance_Estimator___Std;
fix16_t m_Mean_Variance_Estimator___Gamma;
fix16_t m_Mean_Variance_Estimator___Gamma_Initial_Mean;
fix16_t m_Mean_Variance_Estimator___Gamma_Initial_Variance;
fix16_t m_Mean_Variance_Estimator__Gamma_Mean;
fix16_t m_Mean_Variance_Estimator__Gamma_Variance;
fix16_t m_Mean_Variance_Estimator___Uptime_Gamma;
fix16_t m_Mean_Variance_Estimator___Uptime_Gating;
fix16_t m_Mean_Variance_Estimator___Gating_Duration_Minutes;
fix16_t m_Mean_Variance_Estimator___Sigmoid__L;
fix16_t m_Mean_Variance_Estimator___Sigmoid__K;
fix16_t m_Mean_Variance_Estimator___Sigmoid__X0;
fix16_t m_Mox_Model__Sraw_Std;
fix16_t m_Mox_Model__Sraw_Mean;
fix16_t m_Sigmoid_Scaled__Offset;
fix16_t m_Adaptive_Lowpass__A1;
fix16_t m_Adaptive_Lowpass__A2;
bool m_Adaptive_Lowpass___Initialized;
fix16_t m_Adaptive_Lowpass___X1;
fix16_t m_Adaptive_Lowpass___X2;
fix16_t m_Adaptive_Lowpass___X3;
} VocAlgorithmParams;
/**
* Initialize the VOC algorithm parameters. Call this once at the beginning or
* whenever the sensor stopped measurements.
* @param params Pointer to the VocAlgorithmParams struct
*/
void VocAlgorithm_init(VocAlgorithmParams* params);
/**
* Get current algorithm states. Retrieved values can be used in
* VocAlgorithm_set_states() to resume operation after a short interruption,
* skipping initial learning phase. This feature can only be used after at least
* 3 hours of continuous operation.
* @param params Pointer to the VocAlgorithmParams struct
* @param state0 State0 to be stored
* @param state1 State1 to be stored
*/
void VocAlgorithm_get_states(VocAlgorithmParams* params, int32_t* state0,
int32_t* state1);
/**
* Set previously retrieved algorithm states to resume operation after a short
* interruption, skipping initial learning phase. This feature should not be
* used after inerruptions of more than 10 minutes. Call this once after
* VocAlgorithm_init() and the optional VocAlgorithm_set_tuning_parameters(), if
* desired. Otherwise, the algorithm will start with initial learning phase.
* @param params Pointer to the VocAlgorithmParams struct
* @param state0 State0 to be restored
* @param state1 State1 to be restored
*/
void VocAlgorithm_set_states(VocAlgorithmParams* params, int32_t state0,
int32_t state1);
/**
* Set parameters to customize the VOC algorithm. Call this once after
* VocAlgorithm_init(), if desired. Otherwise, the default values will be used.
*
* @param params Pointer to the VocAlgorithmParams struct
* @param voc_index_offset VOC index representing typical (average)
* conditions. Range 1..250, default 100
* @param learning_time_hours Time constant of long-term estimator.
* Past events will be forgotten after about
* twice the learning time.
* Range 1..72 [hours], default 12 [hours]
* @param gating_max_duration_minutes Maximum duration of gating (freeze of
* estimator during high VOC index signal).
* 0 (no gating) or range 1..720 [minutes],
* default 180 [minutes]
* @param std_initial Initial estimate for standard deviation.
* Lower value boosts events during initial
* learning period, but may result in larger
* device-to-device variations.
* Range 10..500, default 50
*/
void VocAlgorithm_set_tuning_parameters(VocAlgorithmParams* params,
int32_t voc_index_offset,
int32_t learning_time_hours,
int32_t gating_max_duration_minutes,
int32_t std_initial);
/**
* Calculate the VOC index value from the raw sensor value.
*
* @param params Pointer to the VocAlgorithmParams struct
* @param sraw Raw value from the SGP40 sensor
* @param voc_index Calculated VOC index value from the raw sensor value. Zero
* during initial blackout period and 1..500 afterwards
*/
void VocAlgorithm_process(VocAlgorithmParams* params, int32_t sraw,
int32_t* voc_index);
#endif /* VOCALGORITHM_H_ */

16
src/time/time.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef time_h
#define time_h
struct strDateTime
{
byte hour;
byte minute;
byte second;
int year;
byte month;
byte day;
byte dayofWeek;
boolean valid;
};
#endif

112
src/touch/gt1151.cpp Normal file
View File

@ -0,0 +1,112 @@
#include "gt1151.h"
#define GT1151_RESET_PIN 02
#define GT1151_INT_PIN 15
GT1151_Dev Dev_Now, Dev_Old;
uint8_t GT_Gesture_Mode = 0;
volatile uint8_t gtIRQ = 0;
void IRAM_ATTR _gt_irq_handler() {
noInterrupts();
gtIRQ = 1;
interrupts();
}
GT1151::GT1151(TwoWire* i2c){ wire = i2c;}
void GT1151::setHandler(void (*handler)(int8_t, GTPoint*)) {
touchHandler = handler;
}
void GT1151::armIRQ() {
attachInterrupt(GT1151_INT_PIN, _gt_irq_handler, RISING);
}
void GT1151::onIRQ() {
int16_t contacts = readInput((GTPoint *) dev.points);
dev.holding = contacts > 0 && dev.pressing;
dev.pressing = contacts > 0;
if (contacts < 0) return;
if (contacts > 0) touchHandler(contacts, (GTPoint *)dev.points);
WriteRegister(GT1151_READ_COORD_ADDR, 0x00);
}
int16_t GT1151::readInput(GTPoint* points) {
uint8_t* data = static_cast<uint8_t*>(static_cast<void*>(points));
int touch_num;
uint8_t regState;
int error = !ReadRegister(GT1151_READ_COORD_ADDR, &regState, 1);
if (!(regState & 0x80)) return -1;
touch_num = regState & 0x0f;
if(touch_num > 5) return -1;
if (touch_num <= 0) return touch_num;
error = !ReadRegister(GT1151_READ_POINTS, data, sizeof(GTPoint) * touch_num);
if (error) return -1;
for(int i = 0; i< touch_num; i++) {
points[i].x = lowByte(points[i].x)<<8 | highByte(points[i].x);
points[i].y = lowByte(points[i].y)<<8 | highByte(points[i].y);
points[i].a = lowByte(points[i].a)<<8 | highByte(points[i].a);
}
return touch_num;
}
void GT1151::Reset(void){
pinMode(GT1151_INT_PIN, OUTPUT);
pinMode(GT1151_INT_PIN, INPUT);
delay(100);
}
void GT1151::ReadVersion(void){
uint8_t buf[4] = {};
ReadRegister(GT1151_REG_ID, buf, 4);
}
void GT1151::begin(void){
delay(500);
Reset();
ReadVersion();
armIRQ();
}
void GT1151::update() {
noInterrupts();
uint8_t irq = gtIRQ;
gtIRQ = 0;
interrupts();
if (irq) onIRQ();
else dev.pressing = false;
}
bool GT1151::WriteRegister(uint16_t addr, uint8_t data){
wire->beginTransmission(GT1151_ADDRESS);
wire->write(highByte(addr));
wire->write(lowByte(addr));
wire->write(data);
wire->endTransmission();
return true;
}
bool GT1151::ReadRegister(uint16_t addr,uint8_t data[],uint8_t length){
uint8_t ord(0);
wire->beginTransmission(GT1151_ADDRESS);
wire->write(highByte(addr));
wire->write(lowByte(addr));
wire->endTransmission();
wire->requestFrom(static_cast<uint8_t>(GT1151_ADDRESS), length);
while(wire->available()){
data[ord++] = wire->read();
}
return ord == length;
}

67
src/touch/gt1151.h Normal file
View File

@ -0,0 +1,67 @@
#include <Arduino.h>
#include <Wire.h>
#ifndef __GT1151_H
#define __GT1151_H
#define GT1151_ADDRESS 0x14
#define GT1151_ADDRESS_28 0x14
#define GT1151_ADDRESS_BA 0x5D
#define CT_MAX_TOUCH 5
#define GT1151_REG_CMD 0x8040
#define GT1151_REG_DATA 0x8140
#define GT1151_REG_ID 0x8140
#define GT1151_READ_COORD_ADDR 0x814E
#define GT1151_READ_POINTS 0x814F
struct GTPoint {
uint8_t id;
uint16_t x;
uint16_t y;
uint16_t a;
};
typedef struct{
bool holding;
bool pressing;
GTPoint points[CT_MAX_TOUCH];
}GT1151_Dev;
class GT1151{
public:
GT1151_Dev dev;
GT1151(TwoWire* i2c);
void update(void);
void begin(void);
void Gesture(void);
void setHandler(void (*handler)(int8_t, GTPoint*));
private:
void (*touchHandler)(int8_t, GTPoint*);
TwoWire* wire;
void Reset(void);
void armIRQ(void);
void onIRQ(void);
int16_t readInput(GTPoint* points);
void ReadVersion(void);
uint8_t Scan(void);
bool WriteRegister(uint16_t addr, uint8_t data);
bool ReadRegister(uint16_t addr,uint8_t data[],uint8_t length);
};
#endif