@ -0,0 +1,41 @@ | |||||
# Number of days of inactivity before an Issue or Pull Request becomes stale | |||||
daysUntilStale: 120 | |||||
# Number of days of inactivity before a stale Issue or Pull Request is closed. | |||||
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. | |||||
daysUntilClose: 30 | |||||
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable | |||||
exemptLabels: | |||||
- enhancement | |||||
- bug | |||||
- staged for release | |||||
# Set to true to ignore issues in a project (defaults to false) | |||||
exemptProjects: false | |||||
# Set to true to ignore issues in a milestone (defaults to false) | |||||
exemptMilestones: false | |||||
# Label to use when marking as stale | |||||
staleLabel: stale | |||||
# Comment to post when marking as stale. Set to `false` to disable | |||||
markComment: > | |||||
This issue has been automatically marked as stale because it has not had | |||||
recent activity. It will be closed in 30 days if no further activity occurs. | |||||
Thank you for your contributions. | |||||
# Comment to post when removing the stale label. | |||||
# unmarkComment: > | |||||
# Your comment here. | |||||
# Comment to post when closing a stale Issue or Pull Request. | |||||
closeComment: > | |||||
This issue will be auto-closed because there hasn't been any activity for a few months. Feel free to open a new one if you still experience this problem. | |||||
# Limit the number of actions per hour, from 1-30. Default is 30 | |||||
limitPerRun: 30 | |||||
# Limit to only `issues` or `pulls` | |||||
only: issues |
@ -1,8 +1,13 @@ | |||||
.clang_complete | .clang_complete | ||||
core_version.h | |||||
custom.h | |||||
.DS_Store | |||||
.gcc-flags.json | .gcc-flags.json | ||||
.pioenvs | .pioenvs | ||||
.piolibdeps | .piolibdeps | ||||
.python-version | |||||
.travis.yml | |||||
.vscode | |||||
.vscode/.browse.c_cpp.db* | |||||
.vscode/c_cpp_properties.json | .vscode/c_cpp_properties.json | ||||
core_version.h | |||||
.pioenvs | |||||
.piolibdeps | |||||
.vscode/launch.json |
@ -0,0 +1,19 @@ | |||||
/* Flash Split for 1M chips, no SPIFFS */ | |||||
/* sketch 995KB */ | |||||
/* eeprom 8KB */ | |||||
/* reserved 16KB */ | |||||
MEMORY | |||||
{ | |||||
dport0_0_seg : org = 0x3FF00000, len = 0x10 | |||||
dram0_0_seg : org = 0x3FFE8000, len = 0x14000 | |||||
iram1_0_seg : org = 0x40100000, len = 0x8000 | |||||
irom0_0_seg : org = 0x40201010, len = 0xf8ff0 | |||||
} | |||||
PROVIDE ( _SPIFFS_start = 0x402FA000 ); | |||||
PROVIDE ( _SPIFFS_end = 0x402FA000 ); | |||||
PROVIDE ( _SPIFFS_page = 0 ); | |||||
PROVIDE ( _SPIFFS_block = 0 ); | |||||
INCLUDE "../ld/eagle.app.v6.common.ld" |
@ -0,0 +1,21 @@ | |||||
/* Flash Split for 4M chips */ | |||||
/* sketch 1019KB */ | |||||
/* empty/ota? 2048KB */ | |||||
/* spiffs 992KB */ | |||||
/* eeprom 16KB */ | |||||
/* reserved 16KB */ | |||||
MEMORY | |||||
{ | |||||
dport0_0_seg : org = 0x3FF00000, len = 0x10 | |||||
dram0_0_seg : org = 0x3FFE8000, len = 0x14000 | |||||
iram1_0_seg : org = 0x40100000, len = 0x8000 | |||||
irom0_0_seg : org = 0x40201010, len = 0xfeff0 | |||||
} | |||||
PROVIDE ( _SPIFFS_start = 0x40500000 ); | |||||
PROVIDE ( _SPIFFS_end = 0x405F8000 ); | |||||
PROVIDE ( _SPIFFS_page = 0x100 ); | |||||
PROVIDE ( _SPIFFS_block = 0x2000 ); | |||||
INCLUDE "../ld/eagle.app.v6.common.ld" |
@ -0,0 +1,20 @@ | |||||
/* Flash Split for 4M chips */ | |||||
/* sketch 1019KB */ | |||||
/* spiffs 3040KB */ | |||||
/* eeprom 16KB */ | |||||
/* reserved 16KB */ | |||||
MEMORY | |||||
{ | |||||
dport0_0_seg : org = 0x3FF00000, len = 0x10 | |||||
dram0_0_seg : org = 0x3FFE8000, len = 0x14000 | |||||
iram1_0_seg : org = 0x40100000, len = 0x8000 | |||||
irom0_0_seg : org = 0x40201010, len = 0xfeff0 | |||||
} | |||||
PROVIDE ( _SPIFFS_start = 0x40300000 ); | |||||
PROVIDE ( _SPIFFS_end = 0x405F8000 ); | |||||
PROVIDE ( _SPIFFS_page = 0x100 ); | |||||
PROVIDE ( _SPIFFS_block = 0x2000 ); | |||||
INCLUDE "../ld/eagle.app.v6.common.ld" |
@ -0,0 +1,18 @@ | |||||
/* Flash Split for 512K chips */ | |||||
/* sketch 487KB */ | |||||
/* eeprom 20KB */ | |||||
MEMORY | |||||
{ | |||||
dport0_0_seg : org = 0x3FF00000, len = 0x10 | |||||
dram0_0_seg : org = 0x3FFE8000, len = 0x14000 | |||||
iram1_0_seg : org = 0x40100000, len = 0x8000 | |||||
irom0_0_seg : org = 0x40201010, len = 0x79ff0 | |||||
} | |||||
PROVIDE ( _SPIFFS_start = 0x4027B000 ); | |||||
PROVIDE ( _SPIFFS_end = 0x4027B000 ); | |||||
PROVIDE ( _SPIFFS_page = 0x0 ); | |||||
PROVIDE ( _SPIFFS_block = 0x0 ); | |||||
INCLUDE "../ld/eagle.app.v6.common.ld" |
@ -0,0 +1,17 @@ | |||||
#pragma once | |||||
// ----------------------------------------------------------------------------- | |||||
// Debug | |||||
// ----------------------------------------------------------------------------- | |||||
#define DEBUG_SUPPORT DEBUG_SERIAL_SUPPORT || DEBUG_UDP_SUPPORT || DEBUG_TELNET_SUPPORT || DEBUG_WEB_SUPPORT | |||||
#if DEBUG_SUPPORT | |||||
#define DEBUG_MSG(...) debugSend(__VA_ARGS__) | |||||
#define DEBUG_MSG_P(...) debugSend_P(__VA_ARGS__) | |||||
#endif | |||||
#ifndef DEBUG_MSG | |||||
#define DEBUG_MSG(...) | |||||
#define DEBUG_MSG_P(...) | |||||
#endif |
@ -0,0 +1,50 @@ | |||||
#pragma once | |||||
//------------------------------------------------------------------------------ | |||||
// Do not change this file unless you know what you are doing | |||||
// Configuration settings are in the general.h file | |||||
//------------------------------------------------------------------------------ | |||||
#if DEBUG_TELNET_SUPPORT | |||||
#undef TELNET_SUPPORT | |||||
#define TELNET_SUPPORT 1 | |||||
#endif | |||||
#if not WEB_SUPPORT | |||||
#undef DEBUG_WEB_SUPPORT | |||||
#define DEBUG_WEB_SUPPORT 0 | |||||
#endif | |||||
#if not WEB_SUPPORT | |||||
#undef SSDP_SUPPORT | |||||
#define SSDP_SUPPORT 0 // SSDP support requires web support | |||||
#endif | |||||
#if UART_MQTT_SUPPORT | |||||
#define MQTT_SUPPORT 1 | |||||
#undef TERMINAL_SUPPORT | |||||
#define TERMINAL_SUPPORT 0 | |||||
#undef DEBUG_SERIAL_SUPPORT | |||||
#define DEBUG_SERIAL_SUPPORT 0 | |||||
#endif | |||||
#if DOMOTICZ_SUPPORT | |||||
#undef MQTT_SUPPORT | |||||
#define MQTT_SUPPORT 1 // If Domoticz enabled enable MQTT | |||||
#endif | |||||
#if HOMEASSISTANT_SUPPORT | |||||
#undef MQTT_SUPPORT | |||||
#define MQTT_SUPPORT 1 // If Home Assistant enabled enable MQTT | |||||
#endif | |||||
#ifndef ASYNC_TCP_SSL_ENABLED | |||||
#if THINGSPEAK_USE_SSL && THINGSPEAK_USE_ASYNC | |||||
#undef THINGSPEAK_SUPPORT // Thingspeak in ASYNC mode requires ASYNC_TCP_SSL_ENABLED | |||||
#endif | |||||
#endif | |||||
#if SCHEDULER_SUPPORT | |||||
#undef NTP_SUPPORT | |||||
#define NTP_SUPPORT 1 // Scheduler needs NTP | |||||
#endif |
@ -0,0 +1,280 @@ | |||||
//-------------------------------------------------------------------------------- | |||||
// PROGMEM definitions | |||||
//-------------------------------------------------------------------------------- | |||||
//-------------------------------------------------------------------------------- | |||||
// Reset reasons | |||||
//-------------------------------------------------------------------------------- | |||||
PROGMEM const char custom_reset_hardware[] = "Hardware button"; | |||||
PROGMEM const char custom_reset_web[] = "Reboot from web interface"; | |||||
PROGMEM const char custom_reset_terminal[] = "Reboot from terminal"; | |||||
PROGMEM const char custom_reset_mqtt[] = "Reboot from MQTT"; | |||||
PROGMEM const char custom_reset_rpc[] = "Reboot from RPC"; | |||||
PROGMEM const char custom_reset_ota[] = "Reboot after successful OTA update"; | |||||
PROGMEM const char custom_reset_http[] = "Reboot from HTTP"; | |||||
PROGMEM const char custom_reset_nofuss[] = "Reboot after successful NoFUSS update"; | |||||
PROGMEM const char custom_reset_upgrade[] = "Reboot after successful web update"; | |||||
PROGMEM const char custom_reset_factory[] = "Factory reset"; | |||||
PROGMEM const char* const custom_reset_string[] = { | |||||
custom_reset_hardware, custom_reset_web, custom_reset_terminal, | |||||
custom_reset_mqtt, custom_reset_rpc, custom_reset_ota, | |||||
custom_reset_http, custom_reset_nofuss, custom_reset_upgrade, | |||||
custom_reset_factory | |||||
}; | |||||
//-------------------------------------------------------------------------------- | |||||
// Capabilities | |||||
//-------------------------------------------------------------------------------- | |||||
PROGMEM const char espurna_modules[] = | |||||
#if ALEXA_SUPPORT | |||||
"ALEXA " | |||||
#endif | |||||
#if BROKER_SUPPORT | |||||
"BROKER " | |||||
#endif | |||||
#if DEBUG_SERIAL_SUPPORT | |||||
"DEBUG_SERIAL " | |||||
#endif | |||||
#if DEBUG_TELNET_SUPPORT | |||||
"DEBUG_TELNET " | |||||
#endif | |||||
#if DEBUG_UDP_SUPPORT | |||||
"DEBUG_UDP " | |||||
#endif | |||||
#if DEBUG_WEB_SUPPORT | |||||
"DEBUG_WEB " | |||||
#endif | |||||
#if DOMOTICZ_SUPPORT | |||||
"DOMOTICZ " | |||||
#endif | |||||
#if HOMEASSISTANT_SUPPORT | |||||
"HOMEASSISTANT " | |||||
#endif | |||||
#if I2C_SUPPORT | |||||
"I2C " | |||||
#endif | |||||
#if INFLUXDB_SUPPORT | |||||
"INFLUXDB " | |||||
#endif | |||||
#if LLMNR_SUPPORT | |||||
"LLMNR " | |||||
#endif | |||||
#if MDNS_SERVER_SUPPORT | |||||
"MDNS_SERVER " | |||||
#endif | |||||
#if MDNS_CLIENT_SUPPORT | |||||
"MDNS_CLIENT " | |||||
#endif | |||||
#if MQTT_SUPPORT | |||||
"MQTT " | |||||
#endif | |||||
#if NETBIOS_SUPPORT | |||||
"NETBIOS " | |||||
#endif | |||||
#if NOFUSS_SUPPORT | |||||
"NOFUSS " | |||||
#endif | |||||
#if NTP_SUPPORT | |||||
"NTP " | |||||
#endif | |||||
#if RF_SUPPORT | |||||
"RF " | |||||
#endif | |||||
#if SCHEDULER_SUPPORT | |||||
"SCHEDULER " | |||||
#endif | |||||
#if SENSOR_SUPPORT | |||||
"SENSOR " | |||||
#endif | |||||
#if SPIFFS_SUPPORT | |||||
"SPIFFS " | |||||
#endif | |||||
#if SSDP_SUPPORT | |||||
"SSDP " | |||||
#endif | |||||
#if TELNET_SUPPORT | |||||
"TELNET " | |||||
#endif | |||||
#if TERMINAL_SUPPORT | |||||
"TERMINAL " | |||||
#endif | |||||
#if THINGSPEAK_SUPPORT | |||||
"THINGSPEAK " | |||||
#endif | |||||
#if UART_MQTT_SUPPORT | |||||
"UART_MQTT " | |||||
#endif | |||||
#if WEB_SUPPORT | |||||
"WEB " | |||||
#endif | |||||
""; | |||||
//-------------------------------------------------------------------------------- | |||||
// Sensors | |||||
//-------------------------------------------------------------------------------- | |||||
#if SENSOR_SUPPORT | |||||
PROGMEM const char espurna_sensors[] = | |||||
#if AM2320_SUPPORT | |||||
"AM2320_I2C " | |||||
#endif | |||||
#if ANALOG_SUPPORT | |||||
"ANALOG " | |||||
#endif | |||||
#if BH1750_SUPPORT | |||||
"BH1750 " | |||||
#endif | |||||
#if BMX280_SUPPORT | |||||
"BMX280 " | |||||
#endif | |||||
#if CSE7766_SUPPORT | |||||
"CSE7766 " | |||||
#endif | |||||
#if DALLAS_SUPPORT | |||||
"DALLAS " | |||||
#endif | |||||
#if DHT_SUPPORT | |||||
"DHTXX " | |||||
#endif | |||||
#if DIGITAL_SUPPORT | |||||
"DIGITAL " | |||||
#endif | |||||
#if ECH1560_SUPPORT | |||||
"ECH1560 " | |||||
#endif | |||||
#if EMON_ADC121_SUPPORT | |||||
"EMON_ADC121 " | |||||
#endif | |||||
#if EMON_ADS1X15_SUPPORT | |||||
"EMON_ADX1X15 " | |||||
#endif | |||||
#if EMON_ANALOG_SUPPORT | |||||
"EMON_ANALOG " | |||||
#endif | |||||
#if EVENTS_SUPPORT | |||||
"EVENTS " | |||||
#endif | |||||
#if GEIGER_SUPPORT | |||||
"GEIGER " | |||||
#endif | |||||
#if GUVAS12SD_SUPPORT | |||||
"GUVAS12SD " | |||||
#endif | |||||
#if HCSR04_SUPPORT | |||||
"HCSR04 " | |||||
#endif | |||||
#if HLW8012_SUPPORT | |||||
"HLW8012 " | |||||
#endif | |||||
#if MHZ19_SUPPORT | |||||
"MHZ19 " | |||||
#endif | |||||
#if PMSX003_SUPPORT | |||||
"PMSX003 " | |||||
#endif | |||||
#if PZEM004T_SUPPORT | |||||
"PZEM004T " | |||||
#endif | |||||
#if SENSEAIR_SUPPORT | |||||
"SENSEAIR " | |||||
#endif | |||||
#if SHT3X_I2C_SUPPORT | |||||
"SHT3X_I2C " | |||||
#endif | |||||
#if SI7021_SUPPORT | |||||
"SI7021 " | |||||
#endif | |||||
#if TMP3X_SUPPORT | |||||
"TMP3X " | |||||
#endif | |||||
#if V9261F_SUPPORT | |||||
"V9261F " | |||||
#endif | |||||
""; | |||||
PROGMEM const unsigned char magnitude_decimals[] = { | |||||
0, | |||||
1, 0, 2, | |||||
3, 0, 0, 0, 0, 0, 0, 0, | |||||
0, 0, 0, | |||||
0, 0, 0, | |||||
0, 0, 3, 3, | |||||
4, 4 // Geiger Counter decimals | |||||
}; | |||||
PROGMEM const char magnitude_unknown_topic[] = "unknown"; | |||||
PROGMEM const char magnitude_temperature_topic[] = "temperature"; | |||||
PROGMEM const char magnitude_humidity_topic[] = "humidity"; | |||||
PROGMEM const char magnitude_pressure_topic[] = "pressure"; | |||||
PROGMEM const char magnitude_current_topic[] = "current"; | |||||
PROGMEM const char magnitude_voltage_topic[] = "voltage"; | |||||
PROGMEM const char magnitude_active_power_topic[] = "power"; | |||||
PROGMEM const char magnitude_apparent_power_topic[] = "apparent"; | |||||
PROGMEM const char magnitude_reactive_power_topic[] = "reactive"; | |||||
PROGMEM const char magnitude_power_factor_topic[] = "factor"; | |||||
PROGMEM const char magnitude_energy_topic[] = "energy"; | |||||
PROGMEM const char magnitude_energy_delta_topic[] = "energy_delta"; | |||||
PROGMEM const char magnitude_analog_topic[] = "analog"; | |||||
PROGMEM const char magnitude_digital_topic[] = "digital"; | |||||
PROGMEM const char magnitude_events_topic[] = "events"; | |||||
PROGMEM const char magnitude_pm1dot0_topic[] = "pm1dot0"; | |||||
PROGMEM const char magnitude_pm2dot5_topic[] = "pm2dot5"; | |||||
PROGMEM const char magnitude_pm10_topic[] = "pm10"; | |||||
PROGMEM const char magnitude_co2_topic[] = "co2"; | |||||
PROGMEM const char magnitude_lux_topic[] = "lux"; | |||||
PROGMEM const char magnitude_uv_topic[] = "uv"; | |||||
PROGMEM const char magnitude_distance_topic[] = "distance"; | |||||
PROGMEM const char magnitude_hcho_topic[] = "hcho"; | |||||
PROGMEM const char magnitude_geiger_cpm_topic[] = "ldr_cpm"; // local dose rate [Counts per minute] | |||||
PROGMEM const char magnitude_geiger_sv_topic[] = "ldr_uSvh"; // local dose rate [µSievert per hour] | |||||
PROGMEM const char* const magnitude_topics[] = { | |||||
magnitude_unknown_topic, magnitude_temperature_topic, magnitude_humidity_topic, | |||||
magnitude_pressure_topic, magnitude_current_topic, magnitude_voltage_topic, | |||||
magnitude_active_power_topic, magnitude_apparent_power_topic, magnitude_reactive_power_topic, | |||||
magnitude_power_factor_topic, magnitude_energy_topic, magnitude_energy_delta_topic, | |||||
magnitude_analog_topic, magnitude_digital_topic, magnitude_events_topic, | |||||
magnitude_pm1dot0_topic, magnitude_pm2dot5_topic, magnitude_pm10_topic, | |||||
magnitude_co2_topic, magnitude_lux_topic, magnitude_uv_topic, | |||||
magnitude_distance_topic, magnitude_hcho_topic, | |||||
magnitude_geiger_cpm_topic, magnitude_geiger_sv_topic // Geiger Counter topics | |||||
}; | |||||
PROGMEM const char magnitude_empty[] = ""; | |||||
PROGMEM const char magnitude_celsius[] = "°C"; | |||||
PROGMEM const char magnitude_fahrenheit[] = "°F"; | |||||
PROGMEM const char magnitude_percentage[] = "%"; | |||||
PROGMEM const char magnitude_hectopascals[] = "hPa"; | |||||
PROGMEM const char magnitude_amperes[] = "A"; | |||||
PROGMEM const char magnitude_volts[] = "V"; | |||||
PROGMEM const char magnitude_watts[] = "W"; | |||||
PROGMEM const char magnitude_kw[] = "kW"; | |||||
PROGMEM const char magnitude_joules[] = "J"; | |||||
PROGMEM const char magnitude_kwh[] = "kWh"; | |||||
PROGMEM const char magnitude_ugm3[] = "µg/m³"; | |||||
PROGMEM const char magnitude_ppm[] = "ppm"; | |||||
PROGMEM const char magnitude_lux[] = "lux"; | |||||
PROGMEM const char magnitude_uv[] = "uv"; | |||||
PROGMEM const char magnitude_distance[] = "m"; | |||||
PROGMEM const char magnitude_mgm3[] = "mg/m³"; | |||||
PROGMEM const char magnitude_geiger_cpm[] = "cpm"; // Counts per Minute: Unit of local dose rate (Geiger counting) | |||||
PROGMEM const char magnitude_geiger_sv[] = "µSv/h"; // µSievert per hour: 2nd unit of local dose rate (Geiger counting) | |||||
PROGMEM const char* const magnitude_units[] = { | |||||
magnitude_empty, magnitude_celsius, magnitude_percentage, | |||||
magnitude_hectopascals, magnitude_amperes, magnitude_volts, | |||||
magnitude_watts, magnitude_watts, magnitude_watts, | |||||
magnitude_percentage, magnitude_joules, magnitude_joules, | |||||
magnitude_empty, magnitude_empty, magnitude_empty, | |||||
magnitude_ugm3, magnitude_ugm3, magnitude_ugm3, | |||||
magnitude_ppm, magnitude_lux, magnitude_uv, | |||||
magnitude_distance, magnitude_mgm3, | |||||
magnitude_geiger_cpm, magnitude_geiger_sv // Geiger counter units | |||||
}; | |||||
#endif |
@ -0,0 +1,302 @@ | |||||
//------------------------------------------------------------------------------ | |||||
// Type definitions | |||||
// Do not touch this definitions | |||||
//------------------------------------------------------------------------------ | |||||
// ----------------------------------------------------------------------------- | |||||
// WIFI | |||||
// ----------------------------------------------------------------------------- | |||||
#define WIFI_STATE_AP 1 | |||||
#define WIFI_STATE_STA 2 | |||||
#define WIFI_STATE_AP_STA 3 | |||||
#define WIFI_STATE_WPS 4 | |||||
#define WIFI_STATE_SMARTCONFIG 8 | |||||
#define WIFI_AP_ALLWAYS 1 | |||||
#define WIFI_AP_FALLBACK 2 | |||||
//------------------------------------------------------------------------------ | |||||
// BUTTONS | |||||
//------------------------------------------------------------------------------ | |||||
#define BUTTON_EVENT_NONE 0 | |||||
#define BUTTON_EVENT_PRESSED 1 | |||||
#define BUTTON_EVENT_RELEASED 2 | |||||
#define BUTTON_EVENT_CLICK 2 | |||||
#define BUTTON_EVENT_DBLCLICK 3 | |||||
#define BUTTON_EVENT_LNGCLICK 4 | |||||
#define BUTTON_EVENT_LNGLNGCLICK 5 | |||||
#define BUTTON_EVENT_TRIPLECLICK 6 | |||||
#define BUTTON_MODE_NONE 0 | |||||
#define BUTTON_MODE_TOGGLE 1 | |||||
#define BUTTON_MODE_ON 2 | |||||
#define BUTTON_MODE_OFF 3 | |||||
#define BUTTON_MODE_AP 4 | |||||
#define BUTTON_MODE_RESET 5 | |||||
#define BUTTON_MODE_PULSE 6 | |||||
#define BUTTON_MODE_FACTORY 7 | |||||
#define BUTTON_MODE_WPS 8 | |||||
#define BUTTON_MODE_SMART_CONFIG 9 | |||||
// Needed for ESP8285 boards under Windows using PlatformIO (?) | |||||
#ifndef BUTTON_PUSHBUTTON | |||||
#define BUTTON_PUSHBUTTON 0 | |||||
#define BUTTON_SWITCH 1 | |||||
#define BUTTON_DEFAULT_HIGH 2 | |||||
#define BUTTON_SET_PULLUP 4 | |||||
#endif | |||||
//------------------------------------------------------------------------------ | |||||
// RELAY | |||||
//------------------------------------------------------------------------------ | |||||
#define RELAY_BOOT_OFF 0 | |||||
#define RELAY_BOOT_ON 1 | |||||
#define RELAY_BOOT_SAME 2 | |||||
#define RELAY_BOOT_TOGGLE 3 | |||||
#define RELAY_TYPE_NORMAL 0 | |||||
#define RELAY_TYPE_INVERSE 1 | |||||
#define RELAY_TYPE_LATCHED 2 | |||||
#define RELAY_TYPE_LATCHED_INVERSE 3 | |||||
#define RELAY_SYNC_ANY 0 | |||||
#define RELAY_SYNC_NONE_OR_ONE 1 | |||||
#define RELAY_SYNC_ONE 2 | |||||
#define RELAY_SYNC_SAME 3 | |||||
#define RELAY_PULSE_NONE 0 | |||||
#define RELAY_PULSE_OFF 1 | |||||
#define RELAY_PULSE_ON 2 | |||||
#define RELAY_PROVIDER_RELAY 0 | |||||
#define RELAY_PROVIDER_DUAL 1 | |||||
#define RELAY_PROVIDER_LIGHT 2 | |||||
#define RELAY_PROVIDER_RFBRIDGE 3 | |||||
#define RELAY_PROVIDER_STM 4 | |||||
//------------------------------------------------------------------------------ | |||||
// UDP SYSLOG | |||||
//------------------------------------------------------------------------------ | |||||
// Priority codes: | |||||
#define SYSLOG_EMERG 0 /* system is unusable */ | |||||
#define SYSLOG_ALERT 1 /* action must be taken immediately */ | |||||
#define SYSLOG_CRIT 2 /* critical conditions */ | |||||
#define SYSLOG_ERR 3 /* error conditions */ | |||||
#define SYSLOG_WARNING 4 /* warning conditions */ | |||||
#define SYSLOG_NOTICE 5 /* normal but significant condition */ | |||||
#define SYSLOG_INFO 6 /* informational */ | |||||
#define SYSLOG_DEBUG 7 /* debug-level messages */ | |||||
// Facility codes: | |||||
#define SYSLOG_KERN (0<<3) /* kernel messages */ | |||||
#define SYSLOG_USER (1<<3) /* random user-level messages */ | |||||
#define SYSLOG_MAIL (2<<3) /* mail system */ | |||||
#define SYSLOG_DAEMON (3<<3) /* system daemons */ | |||||
#define SYSLOG_AUTH (4<<3) /* security/authorization messages */ | |||||
#define SYSLOG_SYSLOG (5<<3) /* messages generated internally by syslogd */ | |||||
#define SYSLOG_LPR (6<<3) /* line printer subsystem */ | |||||
#define SYSLOG_NEWS (7<<3) /* network news subsystem */ | |||||
#define SYSLOG_UUCP (8<<3) /* UUCP subsystem */ | |||||
#define SYSLOG_CRON (9<<3) /* clock daemon */ | |||||
#define SYSLOG_AUTHPRIV (10<<3) /* security/authorization messages (private) */ | |||||
#define SYSLOG_FTP (11<<3) /* ftp daemon */ | |||||
#define SYSLOG_LOCAL0 (16<<3) /* reserved for local use */ | |||||
#define SYSLOG_LOCAL1 (17<<3) /* reserved for local use */ | |||||
#define SYSLOG_LOCAL2 (18<<3) /* reserved for local use */ | |||||
#define SYSLOG_LOCAL3 (19<<3) /* reserved for local use */ | |||||
#define SYSLOG_LOCAL4 (20<<3) /* reserved for local use */ | |||||
#define SYSLOG_LOCAL5 (21<<3) /* reserved for local use */ | |||||
#define SYSLOG_LOCAL6 (22<<3) /* reserved for local use */ | |||||
#define SYSLOG_LOCAL7 (23<<3) /* reserved for local use */ | |||||
//------------------------------------------------------------------------------ | |||||
// MQTT | |||||
//------------------------------------------------------------------------------ | |||||
// Internal MQTT events | |||||
#define MQTT_CONNECT_EVENT 0 | |||||
#define MQTT_DISCONNECT_EVENT 1 | |||||
#define MQTT_MESSAGE_EVENT 2 | |||||
//------------------------------------------------------------------------------ | |||||
// LED | |||||
//------------------------------------------------------------------------------ | |||||
#define LED_MODE_MQTT 0 // LED will be managed from MQTT (OFF by default) | |||||
#define LED_MODE_WIFI 1 // LED will blink according to the WIFI status | |||||
#define LED_MODE_FOLLOW 2 // LED will follow state of linked relay (check RELAY#_LED) | |||||
#define LED_MODE_FOLLOW_INVERSE 3 // LED will follow the opposite state of linked relay (check RELAY#_LED) | |||||
#define LED_MODE_FINDME 4 // LED will be ON if all relays are OFF | |||||
#define LED_MODE_FINDME_WIFI 5 // A mixture between WIFI and FINDME | |||||
#define LED_MODE_ON 6 // LED always ON | |||||
#define LED_MODE_OFF 7 // LED always OFF | |||||
#define LED_MODE_RELAY 8 // If any relay is ON, LED will be ON, otherwise OFF | |||||
#define LED_MODE_RELAY_WIFI 9 // A mixture between WIFI and RELAY, the reverse of MIXED | |||||
// ----------------------------------------------------------------------------- | |||||
// UI | |||||
// ----------------------------------------------------------------------------- | |||||
#define UI_TAG_INPUT 0 | |||||
#define UI_TAG_CHECKBOX 1 | |||||
#define UI_TAG_SELECT 2 | |||||
#define WEB_MODE_NORMAL 0 | |||||
#define WEB_MODE_PASSWORD 1 | |||||
// ----------------------------------------------------------------------------- | |||||
// LIGHT | |||||
// ----------------------------------------------------------------------------- | |||||
// Available light providers | |||||
#define LIGHT_PROVIDER_NONE 0 | |||||
#define LIGHT_PROVIDER_MY92XX 1 // works with MY9291 and MY9231 | |||||
#define LIGHT_PROVIDER_DIMMER 2 | |||||
// ----------------------------------------------------------------------------- | |||||
// SCHEDULER | |||||
// ----------------------------------------------------------------------------- | |||||
#define SCHEDULER_TYPE_SWITCH 1 | |||||
#define SCHEDULER_TYPE_DIM 2 | |||||
// ----------------------------------------------------------------------------- | |||||
// IR | |||||
// ----------------------------------------------------------------------------- | |||||
// IR Button modes | |||||
#define IR_BUTTON_MODE_NONE 0 | |||||
#define IR_BUTTON_MODE_RGB 1 | |||||
#define IR_BUTTON_MODE_HSV 2 | |||||
#define IR_BUTTON_MODE_BRIGHTER 3 | |||||
#define IR_BUTTON_MODE_STATE 4 | |||||
#define IR_BUTTON_MODE_EFFECT 5 | |||||
#define IR_BUTTON_MODE_TOGGLE 6 | |||||
#define LIGHT_EFFECT_SOLID 0 | |||||
#define LIGHT_EFFECT_FLASH 1 | |||||
#define LIGHT_EFFECT_STROBE 2 | |||||
#define LIGHT_EFFECT_FADE 3 | |||||
#define LIGHT_EFFECT_SMOOTH 4 | |||||
//------------------------------------------------------------------------------ | |||||
// RESET | |||||
//------------------------------------------------------------------------------ | |||||
#define CUSTOM_RESET_HARDWARE 1 // Reset from hardware button | |||||
#define CUSTOM_RESET_WEB 2 // Reset from web interface | |||||
#define CUSTOM_RESET_TERMINAL 3 // Reset from terminal | |||||
#define CUSTOM_RESET_MQTT 4 // Reset via MQTT | |||||
#define CUSTOM_RESET_RPC 5 // Reset via RPC (HTTP) | |||||
#define CUSTOM_RESET_OTA 6 // Reset after successful OTA update | |||||
#define CUSTOM_RESET_HTTP 7 // Reset via HTTP GET | |||||
#define CUSTOM_RESET_NOFUSS 8 // Reset after successful NOFUSS update | |||||
#define CUSTOM_RESET_UPGRADE 9 // Reset after update from web interface | |||||
#define CUSTOM_RESET_FACTORY 10 // Factory reset from terminal | |||||
#define CUSTOM_RESET_MAX 10 | |||||
//------------------------------------------------------------------------------ | |||||
// ENVIRONMENTAL | |||||
//------------------------------------------------------------------------------ | |||||
// American Society of Heating, Refrigerating and Air-Conditioning Engineers suggests a range of 45% - 55% humidity to manage health effects and illnesses. | |||||
// Comfortable: 30% - 60% | |||||
// Recommended: 45% - 55% | |||||
// High : 55% - 80% | |||||
#define HUMIDITY_NORMAL 0 // > %30 | |||||
#define HUMIDITY_COMFORTABLE 1 // > %45 | |||||
#define HUMIDITY_DRY 2 // < %30 | |||||
#define HUMIDITY_WET 3 // > %70 | |||||
// United States Environmental Protection Agency - UV Index Scale | |||||
// One UV Index unit is equivalent to 25 milliWatts per square meter. | |||||
#define UV_INDEX_LOW 0 // 0 to 2 means low danger from the sun's UV rays for the average person. | |||||
#define UV_INDEX_MODERATE 1 // 3 to 5 means moderate risk of harm from unprotected sun exposure. | |||||
#define UV_INDEX_HIGH 2 // 6 to 7 means high risk of harm from unprotected sun exposure. Protection against skin and eye damage is needed. | |||||
#define UV_INDEX_VERY_HIGH 3 // 8 to 10 means very high risk of harm from unprotected sun exposure. | |||||
// Take extra precautions because unprotected skin and eyes will be damaged and can burn quickly. | |||||
#define UV_INDEX_EXTREME 4 // 11 or more means extreme risk of harm from unprotected sun exposure. | |||||
// Take all precautions because unprotected skin and eyes can burn in minutes. | |||||
//------------------------------------------------------------------------------ | |||||
// UNITS | |||||
//------------------------------------------------------------------------------ | |||||
#define POWER_WATTS 0 | |||||
#define POWER_KILOWATTS 1 | |||||
#define ENERGY_JOULES 0 | |||||
#define ENERGY_KWH 1 | |||||
#define TMP_CELSIUS 0 | |||||
#define TMP_FAHRENHEIT 1 | |||||
#define TMP_KELVIN 2 | |||||
//-------------------------------------------------------------------------------- | |||||
// Sensor ID | |||||
// These should remain over time, do not modify them, only add new ones at the end | |||||
//-------------------------------------------------------------------------------- | |||||
#define SENSOR_DHTXX_ID 0x01 | |||||
#define SENSOR_DALLAS_ID 0x02 | |||||
#define SENSOR_EMON_ANALOG_ID 0x03 | |||||
#define SENSOR_EMON_ADC121_ID 0x04 | |||||
#define SENSOR_EMON_ADS1X15_ID 0x05 | |||||
#define SENSOR_HLW8012_ID 0x06 | |||||
#define SENSOR_V9261F_ID 0x07 | |||||
#define SENSOR_ECH1560_ID 0x08 | |||||
#define SENSOR_ANALOG_ID 0x09 | |||||
#define SENSOR_DIGITAL_ID 0x10 | |||||
#define SENSOR_EVENTS_ID 0x11 | |||||
#define SENSOR_PMSX003_ID 0x12 | |||||
#define SENSOR_BMX280_ID 0x13 | |||||
#define SENSOR_MHZ19_ID 0x14 | |||||
#define SENSOR_SI7021_ID 0x15 | |||||
#define SENSOR_SHT3X_I2C_ID 0x16 | |||||
#define SENSOR_BH1750_ID 0x17 | |||||
#define SENSOR_PZEM004T_ID 0x18 | |||||
#define SENSOR_AM2320_ID 0x19 | |||||
#define SENSOR_GUVAS12SD_ID 0x20 | |||||
#define SENSOR_CSE7766_ID 0x21 | |||||
#define SENSOR_TMP3X_ID 0x22 | |||||
#define SENSOR_HCSR04_ID 0x23 | |||||
#define SENSOR_SENSEAIR_ID 0x24 | |||||
#define SENSOR_GEIGER_ID 0x25 | |||||
//-------------------------------------------------------------------------------- | |||||
// Magnitudes | |||||
//-------------------------------------------------------------------------------- | |||||
#define MAGNITUDE_NONE 0 | |||||
#define MAGNITUDE_TEMPERATURE 1 | |||||
#define MAGNITUDE_HUMIDITY 2 | |||||
#define MAGNITUDE_PRESSURE 3 | |||||
#define MAGNITUDE_CURRENT 4 | |||||
#define MAGNITUDE_VOLTAGE 5 | |||||
#define MAGNITUDE_POWER_ACTIVE 6 | |||||
#define MAGNITUDE_POWER_APPARENT 7 | |||||
#define MAGNITUDE_POWER_REACTIVE 8 | |||||
#define MAGNITUDE_POWER_FACTOR 9 | |||||
#define MAGNITUDE_ENERGY 10 | |||||
#define MAGNITUDE_ENERGY_DELTA 11 | |||||
#define MAGNITUDE_ANALOG 12 | |||||
#define MAGNITUDE_DIGITAL 13 | |||||
#define MAGNITUDE_EVENTS 14 | |||||
#define MAGNITUDE_PM1dot0 15 | |||||
#define MAGNITUDE_PM2dot5 16 | |||||
#define MAGNITUDE_PM10 17 | |||||
#define MAGNITUDE_CO2 18 | |||||
#define MAGNITUDE_LUX 19 | |||||
#define MAGNITUDE_UV 20 | |||||
#define MAGNITUDE_DISTANCE 21 | |||||
#define MAGNITUDE_HCHO 22 | |||||
#define MAGNITUDE_GEIGER_CPM 23 | |||||
#define MAGNITUDE_GEIGER_SIEVERT 24 | |||||
#define MAGNITUDE_MAX 25 |
@ -1,5 +1,6 @@ | |||||
#define APP_NAME "ESPURNA" | #define APP_NAME "ESPURNA" | ||||
#define APP_VERSION "1.12.4a" | |||||
#define APP_VERSION "1.13.0c" | |||||
#define APP_REVISION "db84006" | |||||
#define APP_AUTHOR "xose.perez@gmail.com" | #define APP_AUTHOR "xose.perez@gmail.com" | ||||
#define APP_WEBSITE "http://tinkerman.cat" | #define APP_WEBSITE "http://tinkerman.cat" | ||||
#define CFG_VERSION 3 | #define CFG_VERSION 3 |
@ -0,0 +1,86 @@ | |||||
/* | |||||
EEPROM MODULE | |||||
*/ | |||||
#include <EEPROM_Rotate.h> | |||||
// ----------------------------------------------------------------------------- | |||||
bool eepromRotate(bool value) { | |||||
// Enable/disable EEPROM rotation only if we are using more sectors than the | |||||
// reserved by the memory layout | |||||
if (EEPROMr.size() > EEPROMr.reserved()) { | |||||
if (value) { | |||||
DEBUG_MSG_P(PSTR("[EEPROM] Reenabling EEPROM rotation\n")); | |||||
} else { | |||||
DEBUG_MSG_P(PSTR("[EEPROM] Disabling EEPROM rotation\n")); | |||||
} | |||||
EEPROMr.rotate(value); | |||||
} | |||||
} | |||||
String eepromSectors() { | |||||
String response; | |||||
for (uint32_t i = 0; i < EEPROMr.size(); i++) { | |||||
if (i > 0) response = response + String(", "); | |||||
response = response + String(EEPROMr.base() - i); | |||||
} | |||||
return response; | |||||
} | |||||
#if TERMINAL_SUPPORT | |||||
void _eepromInitCommands() { | |||||
settingsRegisterCommand(F("EEPROM.DUMP"), [](Embedis* e) { | |||||
EEPROMr.dump(settingsSerial()); | |||||
DEBUG_MSG_P(PSTR("\n+OK\n")); | |||||
}); | |||||
settingsRegisterCommand(F("FLASH.DUMP"), [](Embedis* e) { | |||||
if (e->argc < 2) { | |||||
DEBUG_MSG_P(PSTR("-ERROR: Wrong arguments\n")); | |||||
return; | |||||
} | |||||
uint32_t sector = String(e->argv[1]).toInt(); | |||||
uint32_t max = ESP.getFlashChipSize() / SPI_FLASH_SEC_SIZE; | |||||
if (sector >= max) { | |||||
DEBUG_MSG_P(PSTR("-ERROR: Sector out of range\n")); | |||||
return; | |||||
} | |||||
EEPROMr.dump(settingsSerial(), sector); | |||||
DEBUG_MSG_P(PSTR("\n+OK\n")); | |||||
}); | |||||
} | |||||
#endif | |||||
// ----------------------------------------------------------------------------- | |||||
void eepromSetup() { | |||||
#ifdef EEPROM_ROTATE_SECTORS | |||||
EEPROMr.size(EEPROM_ROTATE_SECTORS); | |||||
#else | |||||
// If the memory layout has more than one sector reserved use those, | |||||
// otherwise calculate pool size based on memory size. | |||||
if (EEPROMr.size() == 1) { | |||||
if (EEPROMr.last() > 1000) { // 4Mb boards | |||||
EEPROMr.size(4); | |||||
} else if (EEPROMr.last() > 250) { // 1Mb boards | |||||
EEPROMr.size(2); | |||||
} | |||||
} | |||||
#endif | |||||
EEPROMr.offset(EEPROM_ROTATE_DATA); | |||||
EEPROMr.begin(EEPROM_SIZE); | |||||
#if TERMINAL_SUPPORT | |||||
_eepromInitCommands(); | |||||
#endif | |||||
} |
@ -0,0 +1,636 @@ | |||||
/** | |||||
* This code is available at | |||||
* http://www.mindspring.com/~pfilandr/C/fs_math/ | |||||
* and it is believed to be public domain. | |||||
*/ | |||||
/* BEGIN fs_math.c */ | |||||
#include "fs_math.h" | |||||
#include <float.h> | |||||
/* | |||||
** pi == (atan(1.0 / 3) + atan(1.0 / 2)) * 4 | |||||
*/ | |||||
static double fs_pi(void); | |||||
static long double fs_pil(void); | |||||
double fs_sqrt(double x) | |||||
{ | |||||
int n; | |||||
double a, b; | |||||
if (x > 0 && DBL_MAX >= x) { | |||||
for (n = 0; x > 2; x /= 4) { | |||||
++n; | |||||
} | |||||
while (0.5 > x) { | |||||
--n; | |||||
x *= 4; | |||||
} | |||||
a = x; | |||||
b = (1 + x) / 2; | |||||
do { | |||||
x = b; | |||||
b = (a / x + x) / 2; | |||||
} while (x > b); | |||||
while (n > 0) { | |||||
x *= 2; | |||||
--n; | |||||
} | |||||
while (0 > n) { | |||||
x /= 2; | |||||
++n; | |||||
} | |||||
} else { | |||||
if (x != 0) { | |||||
x = DBL_MAX; | |||||
} | |||||
} | |||||
return x; | |||||
} | |||||
double fs_log(double x) | |||||
{ | |||||
int n; | |||||
double a, b, c, epsilon; | |||||
static double A, B, C; | |||||
static int initialized; | |||||
if (x > 0 && DBL_MAX >= x) { | |||||
if (!initialized) { | |||||
initialized = 1; | |||||
A = fs_sqrt(2); | |||||
B = A / 2; | |||||
C = fs_log(A); | |||||
} | |||||
for (n = 0; x > A; x /= 2) { | |||||
++n; | |||||
} | |||||
while (B > x) { | |||||
--n; | |||||
x *= 2; | |||||
} | |||||
a = (x - 1) / (x + 1); | |||||
x = C * n + a; | |||||
c = a * a; | |||||
n = 1; | |||||
epsilon = DBL_EPSILON * x; | |||||
if (0 > a) { | |||||
if (epsilon > 0) { | |||||
epsilon = -epsilon; | |||||
} | |||||
do { | |||||
n += 2; | |||||
a *= c; | |||||
b = a / n; | |||||
x += b; | |||||
} while (epsilon > b); | |||||
} else { | |||||
if (0 > epsilon) { | |||||
epsilon = -epsilon; | |||||
} | |||||
do { | |||||
n += 2; | |||||
a *= c; | |||||
b = a / n; | |||||
x += b; | |||||
} while (b > epsilon); | |||||
} | |||||
x *= 2; | |||||
} else { | |||||
x = -DBL_MAX; | |||||
} | |||||
return x; | |||||
} | |||||
double fs_log10(double x) | |||||
{ | |||||
static double log_10; | |||||
static int initialized; | |||||
if (!initialized) { | |||||
initialized = 1; | |||||
log_10 = fs_log(10); | |||||
} | |||||
return x > 0 && DBL_MAX >= x ? fs_log(x) / log_10 : fs_log(x); | |||||
} | |||||
double fs_exp(double x) | |||||
{ | |||||
unsigned n, square; | |||||
double b, e; | |||||
static double x_max, x_min, epsilon; | |||||
static int initialized; | |||||
if (!initialized) { | |||||
initialized = 1; | |||||
x_max = fs_log(DBL_MAX); | |||||
x_min = fs_log(DBL_MIN); | |||||
epsilon = DBL_EPSILON / 4; | |||||
} | |||||
if (x_max >= x && x >= x_min) { | |||||
for (square = 0; x > 1; x /= 2) { | |||||
++square; | |||||
} | |||||
while (-1 > x) { | |||||
++square; | |||||
x /= 2; | |||||
} | |||||
e = b = n = 1; | |||||
do { | |||||
b /= n++; | |||||
b *= x; | |||||
e += b; | |||||
b /= n++; | |||||
b *= x; | |||||
e += b; | |||||
} while (b > epsilon); | |||||
while (square-- != 0) { | |||||
e *= e; | |||||
} | |||||
} else { | |||||
e = x > 0 ? DBL_MAX : 0; | |||||
} | |||||
return e; | |||||
} | |||||
double fs_modf(double value, double *iptr) | |||||
{ | |||||
double a, b; | |||||
const double c = value; | |||||
if (0 > c) { | |||||
value = -value; | |||||
} | |||||
if (DBL_MAX >= value) { | |||||
for (*iptr = 0; value >= 1; value -= b) { | |||||
a = value / 2; | |||||
b = 1; | |||||
while (a >= b) { | |||||
b *= 2; | |||||
} | |||||
*iptr += b; | |||||
} | |||||
} else { | |||||
*iptr = value; | |||||
value = 0; | |||||
} | |||||
if (0 > c) { | |||||
*iptr = -*iptr; | |||||
value = -value; | |||||
} | |||||
return value; | |||||
} | |||||
double fs_fmod(double x, double y) | |||||
{ | |||||
double a, b; | |||||
const double c = x; | |||||
if (0 > c) { | |||||
x = -x; | |||||
} | |||||
if (0 > y) { | |||||
y = -y; | |||||
} | |||||
if (y != 0 && DBL_MAX >= y && DBL_MAX >= x) { | |||||
while (x >= y) { | |||||
a = x / 2; | |||||
b = y; | |||||
while (a >= b) { | |||||
b *= 2; | |||||
} | |||||
x -= b; | |||||
} | |||||
} else { | |||||
x = 0; | |||||
} | |||||
return 0 > c ? -x : x; | |||||
} | |||||
double fs_pow(double x, double y) | |||||
{ | |||||
double p = 0; | |||||
if (0 > x && fs_fmod(y, 1) == 0) { | |||||
if (fs_fmod(y, 2) == 0) { | |||||
p = fs_exp(fs_log(-x) * y); | |||||
} else { | |||||
p = -fs_exp(fs_log(-x) * y); | |||||
} | |||||
} else { | |||||
if (x != 0 || 0 >= y) { | |||||
p = fs_exp(fs_log( x) * y); | |||||
} | |||||
} | |||||
return p; | |||||
} | |||||
static double fs_pi(void) | |||||
{ | |||||
unsigned n; | |||||
double a, b, epsilon; | |||||
static double p; | |||||
static int initialized; | |||||
if (!initialized) { | |||||
initialized = 1; | |||||
epsilon = DBL_EPSILON / 4; | |||||
n = 1; | |||||
a = 3; | |||||
do { | |||||
a /= 9; | |||||
b = a / n; | |||||
n += 2; | |||||
a /= 9; | |||||
b -= a / n; | |||||
n += 2; | |||||
p += b; | |||||
} while (b > epsilon); | |||||
epsilon = DBL_EPSILON / 2; | |||||
n = 1; | |||||
a = 2; | |||||
do { | |||||
a /= 4; | |||||
b = a / n; | |||||
n += 2; | |||||
a /= 4; | |||||
b -= a / n; | |||||
n += 2; | |||||
p += b; | |||||
} while (b > epsilon); | |||||
p *= 4; | |||||
} | |||||
return p; | |||||
} | |||||
double fs_cos(double x) | |||||
{ | |||||
unsigned n; | |||||
int negative, sine; | |||||
double a, b, c; | |||||
static double pi, two_pi, half_pi, third_pi, epsilon; | |||||
static int initialized; | |||||
if (0 > x) { | |||||
x = -x; | |||||
} | |||||
if (DBL_MAX >= x) { | |||||
if (!initialized) { | |||||
initialized = 1; | |||||
pi = fs_pi(); | |||||
two_pi = 2 * pi; | |||||
half_pi = pi / 2; | |||||
third_pi = pi / 3; | |||||
epsilon = DBL_EPSILON / 2; | |||||
} | |||||
if (x > two_pi) { | |||||
x = fs_fmod(x, two_pi); | |||||
} | |||||
if (x > pi) { | |||||
x = two_pi - x; | |||||
} | |||||
if (x > half_pi) { | |||||
x = pi - x; | |||||
negative = 1; | |||||
} else { | |||||
negative = 0; | |||||
} | |||||
if (x > third_pi) { | |||||
x = half_pi - x; | |||||
sine = 1; | |||||
} else { | |||||
sine = 0; | |||||
} | |||||
c = x * x; | |||||
x = n = 0; | |||||
a = 1; | |||||
do { | |||||
b = a; | |||||
a *= c; | |||||
a /= ++n; | |||||
a /= ++n; | |||||
b -= a; | |||||
a *= c; | |||||
a /= ++n; | |||||
a /= ++n; | |||||
x += b; | |||||
} while (b > epsilon); | |||||
if (sine) { | |||||
x = fs_sqrt((1 - x) * (1 + x)); | |||||
} | |||||
if (negative) { | |||||
x = -x; | |||||
} | |||||
} else { | |||||
x = -DBL_MAX; | |||||
} | |||||
return x; | |||||
} | |||||
double fs_log2(double x) | |||||
{ | |||||
static double log_2; | |||||
static int initialized; | |||||
if (!initialized) { | |||||
initialized = 1; | |||||
log_2 = fs_log(2); | |||||
} | |||||
return x > 0 && DBL_MAX >= x ? fs_log(x) / log_2 : fs_log(x); | |||||
} | |||||
double fs_exp2(double x) | |||||
{ | |||||
static double log_2; | |||||
static int initialized; | |||||
if (!initialized) { | |||||
initialized = 1; | |||||
log_2 = fs_log(2); | |||||
} | |||||
return fs_exp(x * log_2); | |||||
} | |||||
long double fs_powl(long double x, long double y) | |||||
{ | |||||
long double p; | |||||
if (0 > x && fs_fmodl(y, 1) == 0) { | |||||
if (fs_fmodl(y, 2) == 0) { | |||||
p = fs_expl(fs_logl(-x) * y); | |||||
} else { | |||||
p = -fs_expl(fs_logl(-x) * y); | |||||
} | |||||
} else { | |||||
if (x != 0 || 0 >= y) { | |||||
p = fs_expl(fs_logl( x) * y); | |||||
} else { | |||||
p = 0; | |||||
} | |||||
} | |||||
return p; | |||||
} | |||||
long double fs_sqrtl(long double x) | |||||
{ | |||||
long int n; | |||||
long double a, b; | |||||
if (x > 0 && LDBL_MAX >= x) { | |||||
for (n = 0; x > 2; x /= 4) { | |||||
++n; | |||||
} | |||||
while (0.5 > x) { | |||||
--n; | |||||
x *= 4; | |||||
} | |||||
a = x; | |||||
b = (1 + x) / 2; | |||||
do { | |||||
x = b; | |||||
b = (a / x + x) / 2; | |||||
} while (x > b); | |||||
while (n > 0) { | |||||
x *= 2; | |||||
--n; | |||||
} | |||||
while (0 > n) { | |||||
x /= 2; | |||||
++n; | |||||
} | |||||
} else { | |||||
if (x != 0) { | |||||
x = LDBL_MAX; | |||||
} | |||||
} | |||||
return x; | |||||
} | |||||
long double fs_logl(long double x) | |||||
{ | |||||
long int n; | |||||
long double a, b, c, epsilon; | |||||
static long double A, B, C; | |||||
static int initialized; | |||||
if (x > 0 && LDBL_MAX >= x) { | |||||
if (!initialized) { | |||||
initialized = 1; | |||||
B = 1.5; | |||||
do { | |||||
A = B; | |||||
B = 1 / A + A / 2; | |||||
} while (A > B); | |||||
B /= 2; | |||||
C = fs_logl(A); | |||||
} | |||||
for (n = 0; x > A; x /= 2) { | |||||
++n; | |||||
} | |||||
while (B > x) { | |||||
--n; | |||||
x *= 2; | |||||
} | |||||
a = (x - 1) / (x + 1); | |||||
x = C * n + a; | |||||
c = a * a; | |||||
n = 1; | |||||
epsilon = LDBL_EPSILON * x; | |||||
if (0 > a) { | |||||
if (epsilon > 0) { | |||||
epsilon = -epsilon; | |||||
} | |||||
do { | |||||
n += 2; | |||||
a *= c; | |||||
b = a / n; | |||||
x += b; | |||||
} while (epsilon > b); | |||||
} else { | |||||
if (0 > epsilon) { | |||||
epsilon = -epsilon; | |||||
} | |||||
do { | |||||
n += 2; | |||||
a *= c; | |||||
b = a / n; | |||||
x += b; | |||||
} while (b > epsilon); | |||||
} | |||||
x *= 2; | |||||
} else { | |||||
x = -LDBL_MAX; | |||||
} | |||||
return x; | |||||
} | |||||
long double fs_expl(long double x) | |||||
{ | |||||
long unsigned n, square; | |||||
long double b, e; | |||||
static long double x_max, x_min, epsilon; | |||||
static int initialized; | |||||
if (!initialized) { | |||||
initialized = 1; | |||||
x_max = fs_logl(LDBL_MAX); | |||||
x_min = fs_logl(LDBL_MIN); | |||||
epsilon = LDBL_EPSILON / 4; | |||||
} | |||||
if (x_max >= x && x >= x_min) { | |||||
for (square = 0; x > 1; x /= 2) { | |||||
++square; | |||||
} | |||||
while (-1 > x) { | |||||
++square; | |||||
x /= 2; | |||||
} | |||||
e = b = n = 1; | |||||
do { | |||||
b /= n++; | |||||
b *= x; | |||||
e += b; | |||||
b /= n++; | |||||
b *= x; | |||||
e += b; | |||||
} while (b > epsilon); | |||||
while (square-- != 0) { | |||||
e *= e; | |||||
} | |||||
} else { | |||||
e = x > 0 ? LDBL_MAX : 0; | |||||
} | |||||
return e; | |||||
} | |||||
static long double fs_pil(void) | |||||
{ | |||||
long unsigned n; | |||||
long double a, b, epsilon; | |||||
static long double p; | |||||
static int initialized; | |||||
if (!initialized) { | |||||
initialized = 1; | |||||
epsilon = LDBL_EPSILON / 4; | |||||
n = 1; | |||||
a = 3; | |||||
do { | |||||
a /= 9; | |||||
b = a / n; | |||||
n += 2; | |||||
a /= 9; | |||||
b -= a / n; | |||||
n += 2; | |||||
p += b; | |||||
} while (b > epsilon); | |||||
epsilon = LDBL_EPSILON / 2; | |||||
n = 1; | |||||
a = 2; | |||||
do { | |||||
a /= 4; | |||||
b = a / n; | |||||
n += 2; | |||||
a /= 4; | |||||
b -= a / n; | |||||
n += 2; | |||||
p += b; | |||||
} while (b > epsilon); | |||||
p *= 4; | |||||
} | |||||
return p; | |||||
} | |||||
long double fs_cosl(long double x) | |||||
{ | |||||
long unsigned n; | |||||
int negative, sine; | |||||
long double a, b, c; | |||||
static long double pi, two_pi, half_pi, third_pi, epsilon; | |||||
static int initialized; | |||||
if (0 > x) { | |||||
x = -x; | |||||
} | |||||
if (LDBL_MAX >= x) { | |||||
if (!initialized) { | |||||
initialized = 1; | |||||
pi = fs_pil(); | |||||
two_pi = 2 * pi; | |||||
half_pi = pi / 2; | |||||
third_pi = pi / 3; | |||||
epsilon = LDBL_EPSILON / 2; | |||||
} | |||||
if (x > two_pi) { | |||||
x = fs_fmodl(x, two_pi); | |||||
} | |||||
if (x > pi) { | |||||
x = two_pi - x; | |||||
} | |||||
if (x > half_pi) { | |||||
x = pi - x; | |||||
negative = 1; | |||||
} else { | |||||
negative = 0; | |||||
} | |||||
if (x > third_pi) { | |||||
x = half_pi - x; | |||||
sine = 1; | |||||
} else { | |||||
sine = 0; | |||||
} | |||||
c = x * x; | |||||
x = n = 0; | |||||
a = 1; | |||||
do { | |||||
b = a; | |||||
a *= c; | |||||
a /= ++n; | |||||
a /= ++n; | |||||
b -= a; | |||||
a *= c; | |||||
a /= ++n; | |||||
a /= ++n; | |||||
x += b; | |||||
} while (b > epsilon); | |||||
if (sine) { | |||||
x = fs_sqrtl((1 - x) * (1 + x)); | |||||
} | |||||
if (negative) { | |||||
x = -x; | |||||
} | |||||
} else { | |||||
x = -LDBL_MAX; | |||||
} | |||||
return x; | |||||
} | |||||
long double fs_fmodl(long double x, long double y) | |||||
{ | |||||
long double a, b; | |||||
const long double c = x; | |||||
if (0 > c) { | |||||
x = -x; | |||||
} | |||||
if (0 > y) { | |||||
y = -y; | |||||
} | |||||
if (y != 0 && LDBL_MAX >= y && LDBL_MAX >= x) { | |||||
while (x >= y) { | |||||
a = x / 2; | |||||
b = y; | |||||
while (a >= b) { | |||||
b *= 2; | |||||
} | |||||
x -= b; | |||||
} | |||||
} else { | |||||
x = 0; | |||||
} | |||||
return 0 > c ? -x : x; | |||||
} | |||||
/* END fs_math.c */ |
@ -0,0 +1,116 @@ | |||||
/** | |||||
* This code is available at | |||||
* http://www.mindspring.com/~pfilandr/C/fs_math/ | |||||
* and it is believed to be public domain. | |||||
*/ | |||||
/* BEGIN fs_math.h */ | |||||
/* | |||||
** Portable freestanding code. | |||||
*/ | |||||
#ifndef H_FS_MATH_H | |||||
#define H_FS_MATH_H | |||||
double fs_sqrt(double x); | |||||
double fs_log(double x); | |||||
double fs_log10(double x); | |||||
/* | |||||
** exp(x) = 1 + x + x^2/2! + x^3/3! + ... | |||||
*/ | |||||
double fs_exp(double x); | |||||
double fs_modf(double value, double *iptr); | |||||
double fs_fmod(double x, double y); | |||||
double fs_pow(double x, double y); | |||||
double fs_cos(double x); | |||||
/* | |||||
** C99 | |||||
*/ | |||||
double fs_log2(double x); | |||||
double fs_exp2(double x); | |||||
long double fs_powl(long double x, long double y); | |||||
long double fs_sqrtl(long double x); | |||||
long double fs_logl(long double x); | |||||
long double fs_expl(long double x); | |||||
long double fs_cosl(long double x); | |||||
long double fs_fmodl(long double x, long double y); | |||||
#endif | |||||
/* END fs_math.h */ | |||||
#if 0 | |||||
/* | |||||
> > Anybody know where I can get some source code for a | |||||
> > reasonably fast double | |||||
> > precision square root algorithm in C. | |||||
> > I'm looking for one that is not IEEE | |||||
> > compliant as I am running on a Z/OS mainframe. | |||||
> > | |||||
> > I would love to use the standard library but | |||||
> > unfortunatly I'm using a | |||||
> > stripped down version of C that looses the the runtime library | |||||
> > (we have to write our own). | |||||
> | |||||
> long double Ssqrt(long double x) | |||||
> { | |||||
> long double a, b; | |||||
> size_t c; | |||||
size_t is a bug here. | |||||
c needs to be a signed type: | |||||
long c; | |||||
> if (x > 0) { | |||||
> c = 0; | |||||
> while (x > 4) { | |||||
> x /= 4; | |||||
> ++c; | |||||
> } | |||||
> while (1.0 / 4 > x) { | |||||
> x *= 4; | |||||
> --c; | |||||
> } | |||||
> a = x; | |||||
> b = ((4 > a) + a) / 2; | |||||
Not a bug, but should be: | |||||
b = (1 + a) / 2; | |||||
> do { | |||||
> x = b; | |||||
> b = (a / x + x) / 2; | |||||
> } while (x > b); | |||||
> if (c > 0) { | |||||
The above line is why c needs to be a signed type, | |||||
otherwise the decremented values of c, are greater than zero, | |||||
and the function won't work if the initial value of x | |||||
is less than 0.25 | |||||
> while (c--) { | |||||
> x *= 2; | |||||
> } | |||||
> } else { | |||||
> while (c++) { | |||||
> x /= 2; | |||||
> } | |||||
> } | |||||
> } | |||||
> return x; | |||||
> } | |||||
> | |||||
> > | |||||
> > That algorithm was actually 4 times slower | |||||
> > then the one below, and more | |||||
> > code. It was accurate though. | |||||
> > | |||||
> | |||||
> Sorry Pete, I wasn't looking very carefully. | |||||
> When I converted your function | |||||
> to double precision it's was much quicker, the best I've seen yet. | |||||
*/ | |||||
#endif |
@ -0,0 +1,198 @@ | |||||
// ----------------------------------------------------------------------------- | |||||
// AM2320 Humidity & Temperature sensor over I2C | |||||
// Copyright (C) 2018 by Mustafa Tufan | |||||
// ----------------------------------------------------------------------------- | |||||
#if SENSOR_SUPPORT && AM2320_SUPPORT | |||||
#pragma once | |||||
#undef I2C_SUPPORT | |||||
#define I2C_SUPPORT 1 // Explicitly request I2C support. | |||||
#include "Arduino.h" | |||||
#include "I2CSensor.h" | |||||
// https://akizukidenshi.com/download/ds/aosong/AM2320.pdf | |||||
#define AM2320_I2C_READ_REGISTER_DATA 0x03 // Read one or more data registers | |||||
#define AM2320_I2C_WRITE_MULTIPLE_REGISTERS 0x10 // Multiple sets of binary data to write multiple registers | |||||
/* | |||||
Register | Address | Register | Address | Register | Address | Register | Address | |||||
-----------------+---------+--------------------+---------+-------------------------+---------+-----------+-------- | |||||
High humidity | 0x00 | Model High | 0x08 | Users register a high | 0x10 | Retention | 0x18 | |||||
Low humidity | 0x01 | Model Low | 0x09 | Users register a low | 0x11 | Retention | 0x19 | |||||
High temperature | 0x02 | The version number | 0x0A | Users register 2 high | 0x12 | Retention | 0x1A | |||||
Low temperature | 0x03 | Device ID(24-31)Bit| 0x0B | Users register 2 low | 0x13 | Retention | 0x1B | |||||
Retention | 0x04 | Device ID(24-31)Bit| 0x0C | Retention | 0x14 | Retention | 0x1C | |||||
Retention | 0x05 | Device ID(24-31)Bit| 0x0D | Retention | 0x15 | Retention | 0x1D | |||||
Retention | 0x06 | Device ID(24-31)Bit| 0x0E | Retention | 0x16 | Retention | 0x1E | |||||
Retention | 0x07 | Status Register | 0x0F | Retention | 0x17 | Retention | 0x1F | |||||
*/ | |||||
class AM2320Sensor : public I2CSensor { | |||||
public: | |||||
// --------------------------------------------------------------------- | |||||
// Public | |||||
// --------------------------------------------------------------------- | |||||
AM2320Sensor(): I2CSensor() { | |||||
_count = 2; | |||||
_sensor_id = SENSOR_AM2320_ID; | |||||
} | |||||
// --------------------------------------------------------------------- | |||||
// Sensor API | |||||
// --------------------------------------------------------------------- | |||||
// Initialization method, must be idempotent | |||||
void begin() { | |||||
if (!_dirty) return; | |||||
// I2C auto-discover | |||||
unsigned char addresses[] = {0x23, 0x5C, 0xB8}; | |||||
_address = _begin_i2c(_address, sizeof(addresses), addresses); | |||||
if (_address == 0) return; | |||||
_ready = true; | |||||
_dirty = false; | |||||
} | |||||
// Descriptive name of the sensor | |||||
String description() { | |||||
char buffer[25]; | |||||
snprintf(buffer, sizeof(buffer), "AM2320 @ I2C (0x%02X)", _address); | |||||
return String(buffer); | |||||
} | |||||
// Descriptive name of the slot # index | |||||
String slot(unsigned char index) { | |||||
return description(); | |||||
}; | |||||
// Type for slot # index | |||||
unsigned char type(unsigned char index) { | |||||
if (index == 0) return MAGNITUDE_TEMPERATURE; | |||||
if (index == 1) return MAGNITUDE_HUMIDITY; | |||||
return MAGNITUDE_NONE; | |||||
} | |||||
// Pre-read hook (usually to populate registers with up-to-date data) | |||||
void pre() { | |||||
_error = SENSOR_ERROR_OK; | |||||
_read(); | |||||
} | |||||
// Current value for slot # index | |||||
double value(unsigned char index) { | |||||
if (index == 0) return _temperature; | |||||
if (index == 1) return _humidity; | |||||
return 0; | |||||
} | |||||
protected: | |||||
// --------------------------------------------------------------------- | |||||
// Protected | |||||
// --------------------------------------------------------------------- | |||||
/* | |||||
// Get device model, version, device_id | |||||
void _init() { | |||||
i2c_wakeup(_address); | |||||
delayMicroseconds(800); | |||||
unsigned char _buffer[11]; | |||||
// 0x08 = read address | |||||
// 7 = number of bytes to read | |||||
if (i2c_write_uint8(_address, AM2320_I2C_READ_REGISTER_DATA, 0x08, 7) != I2C_TRANS_SUCCESS) { | |||||
_error = SENSOR_ERROR_TIMEOUT; | |||||
return false; | |||||
} | |||||
uint16_t model = (_buffer[2] << 8) | _buffer[3]; | |||||
uint8_t version = _buffer[4]; | |||||
uint32_t device_id = _buffer[8] << 24 | _buffer[7] << 16 | _buffer[6] << 8 | _buffer[5]; | |||||
} | |||||
*/ | |||||
void _read() { | |||||
i2c_wakeup(_address); | |||||
// waiting time of at least 800 μs, the maximum 3000 μs | |||||
delayMicroseconds(800); // just to be on safe side | |||||
// 0x00 = read address | |||||
// 4 = number of bytes to read | |||||
if (i2c_write_uint8(_address, AM2320_I2C_READ_REGISTER_DATA, 0x00, 4) != I2C_TRANS_SUCCESS) { | |||||
_error = SENSOR_ERROR_TIMEOUT; | |||||
return; | |||||
} | |||||
unsigned char _buffer[8]; | |||||
// waiting time of at least 800 μs, the maximum 3000 μs | |||||
delayMicroseconds(800 + ((3000-800)/2) ); | |||||
i2c_read_buffer(_address, _buffer, 8); | |||||
// Humidity : 01F4 = (1×256)+(F×16)+4 = 500 => humidity = 500÷10 = 50.0 % | |||||
// 0339 = (3×256)+(3×16)+9 = 825 => humidity = 825÷10 = 82.5 % | |||||
// Temperature: 00FA = (F×16)+A = 250 => temperature = 250÷10 = 25.0 C | |||||
// 0115 = (1×256)+(1×16)+5 = 277 => temperature = 277÷10 = 27.7 C | |||||
// Temperature resolution is 16Bit, temperature highest bit (Bit 15) is equal to 1 indicates a negative temperature | |||||
// _buffer 0 = function code | |||||
// _buffer 1 = number of bytes | |||||
// _buffer 2-3 = high/low humidity | |||||
// _buffer 4-5 = high/low temperature | |||||
// _buffer 6-7 = CRC low/high | |||||
unsigned int responseCRC = 0; | |||||
responseCRC = ((responseCRC | _buffer[7]) << 8 | _buffer[6]); | |||||
if (responseCRC == _CRC16(_buffer)) { | |||||
int foo = (_buffer[2] << 8) | _buffer[3]; | |||||
_humidity = foo / 10.0; | |||||
foo = ((_buffer[4] & 0x7F) << 8) | _buffer[5]; // clean bit 15 and merge | |||||
_temperature = foo / 10.0; | |||||
if (_buffer[4] & 0x80) { // is bit 15 == 1 | |||||
_temperature = _temperature * -1; // negative temperature | |||||
} | |||||
_error = SENSOR_ERROR_OK; | |||||
} else { | |||||
_error = SENSOR_ERROR_CRC; | |||||
return; | |||||
} | |||||
} | |||||
unsigned int _CRC16(unsigned char buffer[]) { | |||||
unsigned int crc16 = 0xFFFF; | |||||
for (unsigned int i = 0; i < 6; i++) { | |||||
crc16 ^= buffer[i]; | |||||
for (unsigned int b = 8; b != 0; b--) { | |||||
if (crc16 & 0x01) { // is lsb set | |||||
crc16 >>= 1; | |||||
crc16 ^= 0xA001; | |||||
} else { | |||||
crc16 >>= 1; | |||||
} | |||||
} | |||||
} | |||||
return crc16; | |||||
} | |||||
double _temperature = 0; | |||||
double _humidity = 0; | |||||
}; | |||||
#endif // SENSOR_SUPPORT && AM2320_SUPPORT |
@ -0,0 +1,378 @@ | |||||
// ----------------------------------------------------------------------------- | |||||
// CSE7766 based power monitor | |||||
// Copyright (C) 2018 by Xose Pérez <xose dot perez at gmail dot com> | |||||
// http://www.chipsea.com/UploadFiles/2017/08/11144342F01B5662.pdf | |||||
// ----------------------------------------------------------------------------- | |||||
#if SENSOR_SUPPORT && CSE7766_SUPPORT | |||||
#pragma once | |||||
#include "Arduino.h" | |||||
#include "BaseSensor.h" | |||||
#include <SoftwareSerial.h> | |||||
class CSE7766Sensor : public BaseSensor { | |||||
public: | |||||
// --------------------------------------------------------------------- | |||||
// Public | |||||
// --------------------------------------------------------------------- | |||||
CSE7766Sensor(): BaseSensor(), _data() { | |||||
_count = 4; | |||||
_sensor_id = SENSOR_CSE7766_ID; | |||||
} | |||||
~CSE7766Sensor() { | |||||
if (_serial) delete _serial; | |||||
} | |||||
// --------------------------------------------------------------------- | |||||
void setRX(unsigned char pin_rx) { | |||||
if (_pin_rx == pin_rx) return; | |||||
_pin_rx = pin_rx; | |||||
_dirty = true; | |||||
} | |||||
void setInverted(bool inverted) { | |||||
if (_inverted == inverted) return; | |||||
_inverted = inverted; | |||||
_dirty = true; | |||||
} | |||||
// --------------------------------------------------------------------- | |||||
unsigned char getRX() { | |||||
return _pin_rx; | |||||
} | |||||
bool getInverted() { | |||||
return _inverted; | |||||
} | |||||
// --------------------------------------------------------------------- | |||||
void expectedCurrent(double expected) { | |||||
if ((expected > 0) && (_current > 0)) { | |||||
_ratioC = _ratioC * (expected / _current); | |||||
} | |||||
} | |||||
void expectedVoltage(unsigned int expected) { | |||||
if ((expected > 0) && (_voltage > 0)) { | |||||
_ratioV = _ratioV * (expected / _voltage); | |||||
} | |||||
} | |||||
void expectedPower(unsigned int expected) { | |||||
if ((expected > 0) && (_active > 0)) { | |||||
_ratioP = _ratioP * (expected / _active); | |||||
} | |||||
} | |||||
void setCurrentRatio(double value) { | |||||
_ratioC = value; | |||||
}; | |||||
void setVoltageRatio(double value) { | |||||
_ratioV = value; | |||||
}; | |||||
void setPowerRatio(double value) { | |||||
_ratioP = value; | |||||
}; | |||||
double getCurrentRatio() { | |||||
return _ratioC; | |||||
}; | |||||
double getVoltageRatio() { | |||||
return _ratioV; | |||||
}; | |||||
double getPowerRatio() { | |||||
return _ratioP; | |||||
}; | |||||
void resetRatios() { | |||||
_ratioC = _ratioV = _ratioP = 1.0; | |||||
} | |||||
void resetEnergy() { | |||||
_energy = 0; | |||||
} | |||||
// --------------------------------------------------------------------- | |||||
// Sensor API | |||||
// --------------------------------------------------------------------- | |||||
// Initialization method, must be idempotent | |||||
void begin() { | |||||
if (!_dirty) return; | |||||
if (_serial) delete _serial; | |||||
if (1 == _pin_rx) { | |||||
Serial.begin(CSE7766_BAUDRATE); | |||||
} else { | |||||
_serial = new SoftwareSerial(_pin_rx, SW_SERIAL_UNUSED_PIN, _inverted, 32); | |||||
_serial->enableIntTx(false); | |||||
_serial->begin(CSE7766_BAUDRATE); | |||||
} | |||||
_ready = true; | |||||
_dirty = false; | |||||
} | |||||
// Descriptive name of the sensor | |||||
String description() { | |||||
char buffer[28]; | |||||
if (1 == _pin_rx) { | |||||
snprintf(buffer, sizeof(buffer), "CSE7766 @ HwSerial"); | |||||
} else { | |||||
snprintf(buffer, sizeof(buffer), "CSE7766 @ SwSerial(%u,NULL)", _pin_rx); | |||||
} | |||||
return String(buffer); | |||||
} | |||||
// Descriptive name of the slot # index | |||||
String slot(unsigned char index) { | |||||
return description(); | |||||
}; | |||||
// Address of the sensor (it could be the GPIO or I2C address) | |||||
String address(unsigned char index) { | |||||
return String(_pin_rx); | |||||
} | |||||
// Loop-like method, call it in your main loop | |||||
void tick() { | |||||
_read(); | |||||
} | |||||
// Type for slot # index | |||||
unsigned char type(unsigned char index) { | |||||
if (index == 0) return MAGNITUDE_CURRENT; | |||||
if (index == 1) return MAGNITUDE_VOLTAGE; | |||||
if (index == 2) return MAGNITUDE_POWER_ACTIVE; | |||||
if (index == 3) return MAGNITUDE_ENERGY; | |||||
return MAGNITUDE_NONE; | |||||
} | |||||
// Current value for slot # index | |||||
double value(unsigned char index) { | |||||
if (index == 0) return _current; | |||||
if (index == 1) return _voltage; | |||||
if (index == 2) return _active; | |||||
if (index == 3) return _energy; | |||||
return 0; | |||||
} | |||||
protected: | |||||
// --------------------------------------------------------------------- | |||||
// Protected | |||||
// --------------------------------------------------------------------- | |||||
/** | |||||
* " | |||||
* Checksum is the sum of all data | |||||
* except for packet header and packet tail lowering by 8bit (...) | |||||
* " | |||||
* @return bool | |||||
*/ | |||||
bool _checksum() { | |||||
unsigned char checksum = 0; | |||||
for (unsigned char i = 2; i < 23; i++) { | |||||
checksum += _data[i]; | |||||
} | |||||
return checksum == _data[23]; | |||||
} | |||||
void _process() { | |||||
// Sample data: | |||||
// 55 5A 02 E9 50 00 03 31 00 3E 9E 00 0D 30 4F 44 F8 00 12 65 F1 81 76 72 (w/ load) | |||||
// F2 5A 02 E9 50 00 03 2B 00 3E 9E 02 D7 7C 4F 44 F8 CF A5 5D E1 B3 2A B4 (w/o load) | |||||
#if SENSOR_DEBUG | |||||
DEBUG_MSG("[SENSOR] CSE7766: _process: "); | |||||
for (byte i=0; i<24; i++) DEBUG_MSG("%02X ", _data[i]); | |||||
DEBUG_MSG("\n"); | |||||
#endif | |||||
// Checksum | |||||
if (!_checksum()) { | |||||
_error = SENSOR_ERROR_CRC; | |||||
#if SENSOR_DEBUG | |||||
DEBUG_MSG("[SENSOR] CSE7766: Checksum error\n"); | |||||
#endif | |||||
return; | |||||
} | |||||
// Calibration | |||||
if (0xAA == _data[0]) { | |||||
_error = SENSOR_ERROR_CALIBRATION; | |||||
#if SENSOR_DEBUG | |||||
DEBUG_MSG("[SENSOR] CSE7766: Chip not calibrated\n"); | |||||
#endif | |||||
return; | |||||
} | |||||
if ((_data[0] & 0xFC) > 0xF0) { | |||||
_error = SENSOR_ERROR_OTHER; | |||||
#if SENSOR_DEBUG | |||||
if (0xF1 == _data[0] & 0xF1) DEBUG_MSG("[SENSOR] CSE7766: Abnormal coefficient storage area\n"); | |||||
if (0xF2 == _data[0] & 0xF2) DEBUG_MSG("[SENSOR] CSE7766: Power cycle exceeded range\n"); | |||||
if (0xF4 == _data[0] & 0xF4) DEBUG_MSG("[SENSOR] CSE7766: Current cycle exceeded range\n"); | |||||
if (0xF8 == _data[0] & 0xF8) DEBUG_MSG("[SENSOR] CSE7766: Voltage cycle exceeded range\n"); | |||||
#endif | |||||
return; | |||||
} | |||||
// Calibration coefficients | |||||
unsigned long _coefV = (_data[2] << 16 | _data[3] << 8 | _data[4] ); // 190770 | |||||
unsigned long _coefC = (_data[8] << 16 | _data[9] << 8 | _data[10]); // 16030 | |||||
unsigned long _coefP = (_data[14] << 16 | _data[15] << 8 | _data[16]); // 5195000 | |||||
// Adj: this looks like a sampling report | |||||
uint8_t adj = _data[20]; // F1 11110001 | |||||
// Calculate voltage | |||||
_voltage = 0; | |||||
if ((adj & 0x40) == 0x40) { | |||||
unsigned long voltage_cycle = _data[5] << 16 | _data[6] << 8 | _data[7]; // 817 | |||||
_voltage = _ratioV * _coefV / voltage_cycle / CSE7766_V2R; // 190700 / 817 = 233.41 | |||||
} | |||||
// Calculate power | |||||
_active = 0; | |||||
if ((adj & 0x10) == 0x10) { | |||||
if ((_data[0] & 0xF2) != 0xF2) { | |||||
unsigned long power_cycle = _data[17] << 16 | _data[18] << 8 | _data[19]; // 4709 | |||||
_active = _ratioP * _coefP / power_cycle / CSE7766_V1R / CSE7766_V2R; // 5195000 / 4709 = 1103.20 | |||||
} | |||||
} | |||||
// Calculate current | |||||
_current = 0; | |||||
if ((adj & 0x20) == 0x20) { | |||||
if (_active > 0) { | |||||
unsigned long current_cycle = _data[11] << 16 | _data[12] << 8 | _data[13]; // 3376 | |||||
_current = _ratioC * _coefC / current_cycle / CSE7766_V1R; // 16030 / 3376 = 4.75 | |||||
} | |||||
} | |||||
// Calculate energy | |||||
unsigned int difference; | |||||
static unsigned int cf_pulses_last = 0; | |||||
unsigned int cf_pulses = _data[21] << 8 | _data[22]; | |||||
if (0 == cf_pulses_last) cf_pulses_last = cf_pulses; | |||||
if (cf_pulses < cf_pulses_last) { | |||||
difference = cf_pulses + (0xFFFF - cf_pulses_last) + 1; | |||||
} else { | |||||
difference = cf_pulses - cf_pulses_last; | |||||
} | |||||
_energy += difference * (float) _coefP / 1000000.0; | |||||
cf_pulses_last = cf_pulses; | |||||
} | |||||
void _read() { | |||||
_error = SENSOR_ERROR_OK; | |||||
static unsigned char index = 0; | |||||
static unsigned long last = millis(); | |||||
while (_serial_available()) { | |||||
// A 24 bytes message takes ~55ms to go through at 4800 bps | |||||
// Reset counter if more than 1000ms have passed since last byte. | |||||
if (millis() - last > CSE7766_SYNC_INTERVAL) index = 0; | |||||
last = millis(); | |||||
uint8_t byte = _serial_read(); | |||||
// first byte must be 0x55 or 0xF? | |||||
if (0 == index) { | |||||
if ((0x55 != byte) && (byte < 0xF0)) { | |||||
continue; | |||||
} | |||||
// second byte must be 0x5A | |||||
} else if (1 == index) { | |||||
if (0x5A != byte) { | |||||
index = 0; | |||||
continue; | |||||
} | |||||
} | |||||
_data[index++] = byte; | |||||
if (index > 23) { | |||||
_serial_flush(); | |||||
break; | |||||
} | |||||
} | |||||
// Process packet | |||||
if (24 == index) { | |||||
_process(); | |||||
index = 0; | |||||
} | |||||
} | |||||
// --------------------------------------------------------------------- | |||||
bool _serial_available() { | |||||
if (1 == _pin_rx) { | |||||
return Serial.available(); | |||||
} else { | |||||
return _serial->available(); | |||||
} | |||||
} | |||||
void _serial_flush() { | |||||
if (1 == _pin_rx) { | |||||
return Serial.flush(); | |||||
} else { | |||||
return _serial->flush(); | |||||
} | |||||
} | |||||
uint8_t _serial_read() { | |||||
if (1 == _pin_rx) { | |||||
return Serial.read(); | |||||
} else { | |||||
return _serial->read(); | |||||
} | |||||
} | |||||
// --------------------------------------------------------------------- | |||||
unsigned int _pin_rx = CSE7766_PIN; | |||||
bool _inverted = CSE7766_PIN_INVERSE; | |||||
SoftwareSerial * _serial = NULL; | |||||
double _active = 0; | |||||
double _voltage = 0; | |||||
double _current = 0; | |||||
double _energy = 0; | |||||
double _ratioV = 1.0; | |||||
double _ratioC = 1.0; | |||||
double _ratioP = 1.0; | |||||
unsigned char _data[24]; | |||||
}; | |||||
#endif // SENSOR_SUPPORT && CSE7766_SUPPORT |
@ -0,0 +1,174 @@ | |||||
// ----------------------------------------------------------------------------- | |||||
// GUVA-S12SD UV Sensor | |||||
// Copyright (C) 2017-2018 by Xose Pérez <xose dot perez at gmail dot com> | |||||
// by Mustafa Tufan | |||||
// ----------------------------------------------------------------------------- | |||||
#if SENSOR_SUPPORT && GUVAS12SD_SUPPORT | |||||
#pragma once | |||||
// Set ADC to TOUT pin | |||||
#undef ADC_MODE_VALUE | |||||
#define ADC_MODE_VALUE ADC_TOUT | |||||
#include "Arduino.h" | |||||
#include "BaseSensor.h" | |||||
// http://www.eoc-inc.com/genicom/GUVA-S12SD.pdf | |||||
// | |||||
// GUVA-S12D has a wide spectral range of 200nm-400nm | |||||
// The output voltage and the UV index is linear, illumination intensity = 307 * Vsig where: Vsig is the value of voltage measured from the SIG pin of the interface, unit V. | |||||
// illumination intensity unit: mW/m2 for the combination strength of UV light with wavelength range: 200nm-400nm | |||||
// UV Index = illumination intensity / 200 | |||||
// | |||||
// UV Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 10+ | |||||
// -----------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+-------- | |||||
// mV | <50 | 227 | 318 | 408 | 503 | 606 | 696 | 795 | 881 | 976 | 1079 | 1170+ | |||||
// analog val | <10 | 46 | 65 | 83 | 103 | 124 | 142 | 162 | 180 | 200 | 221 | 240+ | |||||
// | |||||
#define UV_SAMPLE_RATE 1 | |||||
class GUVAS12SDSensor : public BaseSensor { | |||||
public: | |||||
// --------------------------------------------------------------------- | |||||
// Public | |||||
// --------------------------------------------------------------------- | |||||
GUVAS12SDSensor(): BaseSensor() { | |||||
_count = 1; | |||||
_sensor_id = SENSOR_GUVAS12SD_ID; | |||||
} | |||||
~GUVAS12SDSensor() { | |||||
if (_previous != GPIO_NONE) gpioReleaseLock(_previous); | |||||
} | |||||
// --------------------------------------------------------------------- | |||||
void setGPIO(unsigned char gpio) { | |||||
_gpio = gpio; | |||||
} | |||||
// --------------------------------------------------------------------- | |||||
unsigned char getGPIO() { | |||||
return _gpio; | |||||
} | |||||
// --------------------------------------------------------------------- | |||||
// Sensor API | |||||
// --------------------------------------------------------------------- | |||||
// Initialization method, must be idempotent | |||||
void begin() { | |||||
// Manage GPIO lock | |||||
if (_previous != GPIO_NONE) gpioReleaseLock(_previous); | |||||
_previous = GPIO_NONE; | |||||
if (!gpioGetLock(_gpio)) { | |||||
_error = SENSOR_ERROR_GPIO_USED; | |||||
return; | |||||
} | |||||
_previous = _gpio; | |||||
_ready = true; | |||||
} | |||||
// Pre-read hook (usually to populate registers with up-to-date data) | |||||
void pre() { | |||||
_error = SENSOR_ERROR_OK; | |||||
_read(); | |||||
} | |||||
// Descriptive name of the sensor | |||||
String description() { | |||||
char buffer[18]; | |||||
snprintf(buffer, sizeof(buffer), "GUVAS12SD @ GPIO%d", _gpio); | |||||
return String(buffer); | |||||
} | |||||
// Descriptive name of the slot # index | |||||
String slot(unsigned char index) { | |||||
return description(); | |||||
}; | |||||
// Address of the sensor (it could be the GPIO or I2C address) | |||||
String address(unsigned char index) { | |||||
return String(_gpio); | |||||
} | |||||
// Type for slot # index | |||||
unsigned char type(unsigned char index) { | |||||
if (index == 0) return MAGNITUDE_UV; | |||||
return MAGNITUDE_NONE; | |||||
} | |||||
// Current value for slot # index | |||||
double value(unsigned char index) { | |||||
if (index == 0) return _uvindex; | |||||
return 0; | |||||
} | |||||
protected: | |||||
// --------------------------------------------------------------------- | |||||
// Protected | |||||
// --------------------------------------------------------------------- | |||||
void _read() { | |||||
int _average = 0; | |||||
#if UV_SAMPLE_RATE == 1 | |||||
_average = analogRead(0); | |||||
#else | |||||
for (unsigned int i=0; i < UV_SAMPLE_RATE; i++) { | |||||
_average += analogRead(0); | |||||
nice_delay(2); | |||||
} | |||||
_average = (_average / UV_SAMPLE_RATE); | |||||
#endif | |||||
// _sensormV = _average / 1023*3.3; | |||||
if (_average < 10) { | |||||
_uvindex = 0; | |||||
} else if (_average < 46) { | |||||
_uvindex = (_average - 10) / (46-10); | |||||
} else if (_average < 65) { | |||||
_uvindex = 1 + ((_average - 46) / (65-46)); | |||||
} else if (_average < 83) { | |||||
_uvindex = 2 + ((_average - 65) / (83-65)); | |||||
} else if (_average < 103) { | |||||
_uvindex = 3 + ((_average - 83) / (103- 83)); | |||||
} else if (_average < 124) { | |||||
_uvindex = 4 + ((_average - 103) / (124-103)); | |||||
} else if (_average < 142) { | |||||
_uvindex = 5 + ((_average - 124) / (142-124)); | |||||
} else if (_average < 162) { | |||||
_uvindex = 6 + ((_average - 142) / (162-142)); | |||||
} else if (_average < 180) { | |||||
_uvindex = 7 + ((_average - 162) / (180-162)); | |||||
} else if (_average < 200) { | |||||
_uvindex = 8 + ((_average - 180) / (200-180)); | |||||
} else if (_average < 221) { | |||||
_uvindex = 9 + ((_average - 200) / (221-200)); | |||||
} else { | |||||
_uvindex = 10; | |||||
} | |||||
} | |||||
unsigned char _gpio = GPIO_NONE; | |||||
unsigned char _previous = GPIO_NONE; | |||||
double _uvindex = 0; | |||||
}; | |||||
#endif // SENSOR_SUPPORT && GUVAS12SD_SUPPORT |
@ -0,0 +1,298 @@ | |||||
// ----------------------------------------------------------------------------- | |||||
// Geiger Sensor based on Event Counter Sensor | |||||
// Copyright (C) 2018 by Sven Kopetzki <skopetzki at web dot de> | |||||
// Documentation: https://github.com/Trickx/espurna/wiki/Geiger-counter | |||||
// ----------------------------------------------------------------------------- | |||||
#if SENSOR_SUPPORT && GEIGER_SUPPORT | |||||
#pragma once | |||||
#include "Arduino.h" | |||||
#include "BaseSensor.h" | |||||
class GeigerSensor : public BaseSensor { | |||||
public: | |||||
// --------------------------------------------------------------------- | |||||
// Public | |||||
// --------------------------------------------------------------------- | |||||
GeigerSensor() : BaseSensor() { | |||||
_count = 2; | |||||
_sensor_id = SENSOR_GEIGER_ID; | |||||
} | |||||
~GeigerSensor() { | |||||
_enableInterrupts(false); | |||||
} | |||||
// --------------------------------------------------------------------- | |||||
void setGPIO(unsigned char gpio) { | |||||
_gpio = gpio; | |||||
} | |||||
void setMode(unsigned char mode) { | |||||
_mode = mode; | |||||
} | |||||
void setInterruptMode(unsigned char mode) { | |||||
_interrupt_mode = mode; | |||||
} | |||||
void setDebounceTime(unsigned long debounce) { | |||||
_debounce = debounce; | |||||
} | |||||
void setCPM2SievertFactor(unsigned int cpm2sievert) { | |||||
_cpm2sievert = cpm2sievert; | |||||
} | |||||
// --------------------------------------------------------------------- | |||||
unsigned char getGPIO() { | |||||
return _gpio; | |||||
} | |||||
unsigned char getMode() { | |||||
return _mode; | |||||
} | |||||
unsigned char getInterruptMode() { | |||||
return _interrupt_mode; | |||||
} | |||||
unsigned long getDebounceTime() { | |||||
return _debounce; | |||||
} | |||||
unsigned long getCPM2SievertFactor() { | |||||
return _cpm2sievert; | |||||
} | |||||
// --------------------------------------------------------------------- | |||||
// Sensors API | |||||
// --------------------------------------------------------------------- | |||||
// Initialization method, must be idempotent | |||||
// Defined outside the class body | |||||
void begin() { | |||||
pinMode(_gpio, _mode); | |||||
_enableInterrupts(true); | |||||
_ready = true; | |||||
} | |||||
// Descriptive name of the sensor | |||||
String description() { | |||||
char buffer[20]; | |||||
snprintf(buffer, sizeof(buffer), "µSv/h @ GPIO%d", _gpio); | |||||
return String(buffer); | |||||
} | |||||
// Descriptive name of the slot # index | |||||
String slot(unsigned char index) { | |||||
char buffer[30]; | |||||
unsigned char i=0; | |||||
#if GEIGER_REPORT_CPM | |||||
if (index == i++) { | |||||
snprintf(buffer, sizeof(buffer), "Counts per Minute @ GPIO%d", _gpio); | |||||
return String(buffer); | |||||
} | |||||
#endif | |||||
#if GEIGER_REPORT_SIEVERTS | |||||
if (index == i++) { | |||||
snprintf(buffer, sizeof(buffer), "CPM / %d = µSv/h", _cpm2sievert); | |||||
return String(buffer); | |||||
} | |||||
#endif | |||||
snprintf(buffer, sizeof(buffer), "Events @ GPIO%d", _gpio); | |||||
return String(buffer); | |||||
}; | |||||
// Address of the sensor (it could be the GPIO or I2C address) | |||||
String address(unsigned char index) { | |||||
return String(_gpio); | |||||
} | |||||
// Type for slot # index | |||||
unsigned char type(unsigned char index) { | |||||
unsigned char i=0; | |||||
#if GEIGER_REPORT_CPM | |||||
if (index == i++) return MAGNITUDE_GEIGER_CPM; | |||||
#endif | |||||
#if GEIGER_REPORT_SIEVERTS | |||||
if (index == i++) return MAGNITUDE_GEIGER_SIEVERT; | |||||
#endif | |||||
return MAGNITUDE_NONE; | |||||
} | |||||
// Current value for slot # index | |||||
double value(unsigned char index) { | |||||
unsigned char i=0; | |||||
#if GEIGER_REPORT_CPM | |||||
if (index == i++) { | |||||
unsigned long _period_begin = _lastreport_cpm; | |||||
_lastreport_cpm = millis(); | |||||
double value = _events * 60000; | |||||
value = value / (_lastreport_cpm-_period_begin); | |||||
#if SENSOR_DEBUG | |||||
char data[128]; char buffer[10]; | |||||
dtostrf(value, 1-sizeof(buffer), 4, buffer); | |||||
snprintf(data, sizeof(data), "Ticks: %u | Interval: %u | CPM: %s", _ticks, (_lastreport_cpm-_period_begin), buffer); | |||||
DEBUG_MSG("[GEIGER] %s\n", data); | |||||
#endif | |||||
_events = 0; | |||||
return value; | |||||
} | |||||
#endif | |||||
#if GEIGER_REPORT_SIEVERTS | |||||
if (index == i++) { | |||||
unsigned long _period_begin = _lastreport_sv; | |||||
_lastreport_sv = millis(); | |||||
double value = _ticks * 60000 / _cpm2sievert; | |||||
value = value / (_lastreport_sv-_period_begin); | |||||
#if SENSOR_DEBUG | |||||
char data[128]; char buffer[10]; | |||||
dtostrf(value, 1-sizeof(buffer), 4, buffer); | |||||
snprintf(data, sizeof(data), "Ticks: %u | Interval: %u | µSievert: %s", _ticks, (_lastreport_sv-_period_begin), buffer); | |||||
DEBUG_MSG("[GEIGER] %s\n", data); | |||||
#endif | |||||
_ticks = 0; | |||||
return value; | |||||
} | |||||
#endif | |||||
return 0; | |||||
} | |||||
// Handle interrupt calls | |||||
void handleInterrupt(unsigned char gpio) { | |||||
(void) gpio; | |||||
static unsigned long last = 0; | |||||
if (millis() - last > _debounce) { | |||||
_events = _events + 1; | |||||
_ticks = _ticks + 1; | |||||
last = millis(); | |||||
} | |||||
} | |||||
protected: | |||||
// --------------------------------------------------------------------- | |||||
// Interrupt management | |||||
// --------------------------------------------------------------------- | |||||
void _attach(GeigerSensor * instance, unsigned char gpio, unsigned char mode); | |||||
void _detach(unsigned char gpio); | |||||
void _enableInterrupts(bool value) { | |||||
static unsigned char _interrupt_gpio = GPIO_NONE; | |||||
if (value) { | |||||
if (_interrupt_gpio != GPIO_NONE) _detach(_interrupt_gpio); | |||||
_attach(this, _gpio, _interrupt_mode); | |||||
_interrupt_gpio = _gpio; | |||||
} else if (_interrupt_gpio != GPIO_NONE) { | |||||
_detach(_interrupt_gpio); | |||||
_interrupt_gpio = GPIO_NONE; | |||||
} | |||||
} | |||||
// --------------------------------------------------------------------- | |||||
// Protected | |||||
// --------------------------------------------------------------------- | |||||
volatile unsigned long _events = 0; | |||||
volatile unsigned long _ticks = 0; | |||||
unsigned long _debounce = GEIGER_DEBOUNCE; | |||||
unsigned int _cpm2sievert = GEIGER_CPM2SIEVERT; | |||||
unsigned char _gpio; | |||||
unsigned char _mode; | |||||
unsigned char _interrupt_mode; | |||||
// Added for µSievert calculations | |||||
unsigned long _lastreport_cpm = millis(); | |||||
unsigned long _lastreport_sv = _lastreport_cpm; | |||||
}; | |||||
// ----------------------------------------------------------------------------- | |||||
// Interrupt helpers | |||||
// ----------------------------------------------------------------------------- | |||||
GeigerSensor * _geiger_sensor_instance[10] = {NULL}; | |||||
void ICACHE_RAM_ATTR _geiger_sensor_isr(unsigned char gpio) { | |||||
unsigned char index = gpio > 5 ? gpio-6 : gpio; | |||||
if (_geiger_sensor_instance[index]) { | |||||
_geiger_sensor_instance[index]->handleInterrupt(gpio); | |||||
} | |||||
} | |||||
void ICACHE_RAM_ATTR _geiger_sensor_isr_0() { | |||||
_geiger_sensor_isr(0); | |||||
} | |||||
void ICACHE_RAM_ATTR _geiger_sensor_isr_1() { | |||||
_geiger_sensor_isr(1); | |||||
} | |||||
void ICACHE_RAM_ATTR _geiger_sensor_isr_2() { | |||||
_geiger_sensor_isr(2); | |||||
} | |||||
void ICACHE_RAM_ATTR _geiger_sensor_isr_3() { | |||||
_geiger_sensor_isr(3); | |||||
} | |||||
void ICACHE_RAM_ATTR _geiger_sensor_isr_4() { | |||||
_geiger_sensor_isr(4); | |||||
} | |||||
void ICACHE_RAM_ATTR _geiger_sensor_isr_5() { | |||||
_geiger_sensor_isr(5); | |||||
} | |||||
void ICACHE_RAM_ATTR _geiger_sensor_isr_12() { | |||||
_geiger_sensor_isr(12); | |||||
} | |||||
void ICACHE_RAM_ATTR _geiger_sensor_isr_13() { | |||||
_geiger_sensor_isr(13); | |||||
} | |||||
void ICACHE_RAM_ATTR _geiger_sensor_isr_14() { | |||||
_geiger_sensor_isr(14); | |||||
} | |||||
void ICACHE_RAM_ATTR _geiger_sensor_isr_15() { | |||||
_geiger_sensor_isr(15); | |||||
} | |||||
static void (*_geiger_sensor_isr_list[10])() = { | |||||
_geiger_sensor_isr_0, _geiger_sensor_isr_1, _geiger_sensor_isr_2, | |||||
_geiger_sensor_isr_3, _geiger_sensor_isr_4, _geiger_sensor_isr_5, | |||||
_geiger_sensor_isr_12, _geiger_sensor_isr_13, _geiger_sensor_isr_14, | |||||
_geiger_sensor_isr_15 | |||||
}; | |||||
void GeigerSensor::_attach(GeigerSensor * instance, unsigned char gpio, unsigned char mode) { | |||||
if (!gpioValid(gpio)) return; | |||||
_detach(gpio); | |||||
unsigned char index = gpio > 5 ? gpio-6 : gpio; | |||||
_geiger_sensor_instance[index] = instance; | |||||
attachInterrupt(gpio, _geiger_sensor_isr_list[index], mode); | |||||
#if SENSOR_DEBUG | |||||
DEBUG_MSG_P(PSTR("[GEIGER] GPIO%d interrupt attached to %s\n"), gpio, instance->description().c_str()); | |||||
#endif | |||||
} | |||||
void GeigerSensor::_detach(unsigned char gpio) { | |||||
if (!gpioValid(gpio)) return; | |||||
unsigned char index = gpio > 5 ? gpio-6 : gpio; | |||||
if (_geiger_sensor_instance[index]) { | |||||
detachInterrupt(gpio); | |||||
#if SENSOR_DEBUG | |||||
DEBUG_MSG_P(PSTR("[GEIGER] GPIO%d interrupt detached from %s\n"), gpio, _geiger_sensor_instance[index]->description().c_str()); | |||||
#endif | |||||
_geiger_sensor_instance[index] = NULL; | |||||
} | |||||
} | |||||
#endif // SENSOR_SUPPORT && GEIGER_SUPPORT |
@ -0,0 +1,119 @@ | |||||
// ----------------------------------------------------------------------------- | |||||
// HC-SR04 Ultrasonic sensor | |||||
// Copyright (C) 2018 by Xose Pérez <xose dot perez at gmail dot com> | |||||
// ----------------------------------------------------------------------------- | |||||
#if SENSOR_SUPPORT && HCSR04_SUPPORT | |||||
#pragma once | |||||
#include "Arduino.h" | |||||
#include "BaseSensor.h" | |||||
class HCSR04Sensor : public BaseSensor { | |||||
public: | |||||
// --------------------------------------------------------------------- | |||||
// Public | |||||
// --------------------------------------------------------------------- | |||||
HCSR04Sensor(): BaseSensor() { | |||||
_count = 1; | |||||
_sensor_id = SENSOR_HCSR04_ID; | |||||
} | |||||
// --------------------------------------------------------------------- | |||||
void setEcho(unsigned char echo) { | |||||
_echo = echo; | |||||
} | |||||
void setTrigger(unsigned char trigger) { | |||||
_trigger = trigger; | |||||
} | |||||
// --------------------------------------------------------------------- | |||||
unsigned char getEcho() { | |||||
return _echo; | |||||
} | |||||
unsigned char getTrigger() { | |||||
return _trigger; | |||||
} | |||||
// --------------------------------------------------------------------- | |||||
// Sensor API | |||||
// --------------------------------------------------------------------- | |||||
// Initialization method, must be idempotent | |||||
void begin() { | |||||
pinMode(_echo, INPUT); | |||||
pinMode(_trigger, OUTPUT); | |||||
digitalWrite(_trigger, LOW); | |||||
_ready = true; | |||||
} | |||||
// Descriptive name of the sensor | |||||
String description() { | |||||
char buffer[24]; | |||||
snprintf(buffer, sizeof(buffer), "HCSR04 @ GPIO(%u, %u)", _trigger, _echo); | |||||
return String(buffer); | |||||
} | |||||
// Descriptive name of the slot # index | |||||
String slot(unsigned char index) { | |||||
return description(); | |||||
}; | |||||
// Address of the sensor (it could be the GPIO or I2C address) | |||||
String address(unsigned char index) { | |||||
return String(_trigger); | |||||
} | |||||
// Type for slot # index | |||||
unsigned char type(unsigned char index) { | |||||
if (index == 0) return MAGNITUDE_DISTANCE; | |||||
return MAGNITUDE_NONE; | |||||
} | |||||
// Current value for slot # index | |||||
double value(unsigned char index) { | |||||
if (index == 0) { | |||||
// Trigger pulse | |||||
digitalWrite(_trigger, HIGH); | |||||
delayMicroseconds(10); | |||||
digitalWrite(_trigger, LOW); | |||||
// Wait for echo pulse low-high-low | |||||
while ( digitalRead(_echo) == 0 ) yield(); | |||||
unsigned long start = micros(); | |||||
while ( digitalRead(_echo) == 1 ) yield(); | |||||
unsigned long travel_time = micros() - start; | |||||
// Assuming a speed of sound of 340m/s | |||||
// Dividing by 2 since it is a round trip | |||||
return 340.0 * (double) travel_time / 1000000.0 / 2; | |||||
} | |||||
return 0; | |||||
} | |||||
protected: | |||||
// --------------------------------------------------------------------- | |||||
// Protected | |||||
// --------------------------------------------------------------------- | |||||
unsigned char _trigger; | |||||
unsigned char _echo; | |||||
}; | |||||
#endif // SENSOR_SUPPORT && HCSR04_SUPPORT |
@ -0,0 +1,133 @@ | |||||
// ----------------------------------------------------------------------------- | |||||
// PZEM004T based power monitor | |||||
// Copyright (C) 2018 by Xose Pérez <xose dot perez at gmail dot com> | |||||
// ----------------------------------------------------------------------------- | |||||
#if SENSOR_SUPPORT && PZEM004T_SUPPORT | |||||
#pragma once | |||||
#include "Arduino.h" | |||||
#include "BaseSensor.h" | |||||
#include <PZEM004T.h> | |||||
class PZEM004TSensor : public BaseSensor { | |||||
public: | |||||
// --------------------------------------------------------------------- | |||||
// Public | |||||
// --------------------------------------------------------------------- | |||||
PZEM004TSensor(): BaseSensor() { | |||||
_count = 4; | |||||
_sensor_id = SENSOR_PZEM004T_ID; | |||||
_ip = IPAddress(192,168,1,1); | |||||
} | |||||
~PZEM004TSensor() { | |||||
if (_pzem) delete _pzem; | |||||
} | |||||
// --------------------------------------------------------------------- | |||||
void setRX(unsigned char pin_rx) { | |||||
if (_pin_rx == pin_rx) return; | |||||
_pin_rx = pin_rx; | |||||
_dirty = true; | |||||
} | |||||
void setTX(unsigned char pin_tx) { | |||||
if (_pin_tx == pin_tx) return; | |||||
_pin_tx = pin_tx; | |||||
_dirty = true; | |||||
} | |||||
void setSerial(HardwareSerial * serial) { | |||||
_serial = serial; | |||||
_dirty = true; | |||||
} | |||||
// --------------------------------------------------------------------- | |||||
unsigned char getRX() { | |||||
return _pin_rx; | |||||
} | |||||
unsigned char getTX() { | |||||
return _pin_tx; | |||||
} | |||||
// --------------------------------------------------------------------- | |||||
// Sensor API | |||||
// --------------------------------------------------------------------- | |||||
// Initialization method, must be idempotent | |||||
void begin() { | |||||
if (!_dirty) return; | |||||
if (_pzem) delete _pzem; | |||||
if (_serial) { | |||||
_pzem = new PZEM004T(_serial); | |||||
} else { | |||||
_pzem = new PZEM004T(_pin_rx, _pin_tx); | |||||
} | |||||
_pzem->setAddress(_ip); | |||||
_ready = true; | |||||
_dirty = false; | |||||
} | |||||
// Descriptive name of the sensor | |||||
String description() { | |||||
char buffer[28]; | |||||
snprintf(buffer, sizeof(buffer), "PZEM004T @ SwSerial(%u,%u)", _pin_rx, _pin_tx); | |||||
return String(buffer); | |||||
} | |||||
// Descriptive name of the slot # index | |||||
String slot(unsigned char index) { | |||||
return description(); | |||||
}; | |||||
// Address of the sensor (it could be the GPIO or I2C address) | |||||
String address(unsigned char index) { | |||||
return _ip.toString(); | |||||
} | |||||
// Type for slot # index | |||||
unsigned char type(unsigned char index) { | |||||
if (index == 0) return MAGNITUDE_CURRENT; | |||||
if (index == 1) return MAGNITUDE_VOLTAGE; | |||||
if (index == 2) return MAGNITUDE_POWER_ACTIVE; | |||||
if (index == 3) return MAGNITUDE_ENERGY; | |||||
return MAGNITUDE_NONE; | |||||
} | |||||
// Current value for slot # index | |||||
double value(unsigned char index) { | |||||
if (index == 0) return _pzem->current(_ip); | |||||
if (index == 1) return _pzem->voltage(_ip); | |||||
if (index == 2) return _pzem->power(_ip); | |||||
if (index == 3) return _pzem->energy(_ip); | |||||
return 0; | |||||
} | |||||
protected: | |||||
// --------------------------------------------------------------------- | |||||
// Protected | |||||
// --------------------------------------------------------------------- | |||||
unsigned int _pin_rx = PZEM004T_RX_PIN; | |||||
unsigned int _pin_tx = PZEM004T_TX_PIN; | |||||
IPAddress _ip; | |||||
HardwareSerial * _serial = NULL; | |||||
PZEM004T * _pzem = NULL; | |||||
}; | |||||
#endif // SENSOR_SUPPORT && PZEM004T_SUPPORT |
@ -0,0 +1,233 @@ | |||||
// ----------------------------------------------------------------------------- | |||||
// SenseAir S8 CO2 Sensor | |||||
// Uses SoftwareSerial library | |||||
// Contribution by Yonsm Guo | |||||
// ----------------------------------------------------------------------------- | |||||
#if SENSOR_SUPPORT && SENSEAIR_SUPPORT | |||||
#pragma once | |||||
#include "Arduino.h" | |||||
#include "BaseSensor.h" | |||||
#include <SoftwareSerial.h> | |||||
// SenseAir sensor utils | |||||
class SenseAir | |||||
{ | |||||
protected: | |||||
SoftwareSerial *_serial; // Should initialized by child class | |||||
public: | |||||
int sendCommand(byte command[]) { | |||||
byte recv_buf[7] = {0xff}; | |||||
byte data_buf[2] = {0xff}; | |||||
long value = -1; | |||||
_serial->write(command, 8); //Send the byte array | |||||
delay(50); | |||||
// Read answer from sensor | |||||
int ByteCounter = 0; | |||||
while(_serial->available()) { | |||||
recv_buf[ByteCounter] = _serial->read(); | |||||
ByteCounter++; | |||||
} | |||||
data_buf[0] = recv_buf[3]; | |||||
data_buf[1] = recv_buf[4]; | |||||
value = (data_buf[0] << 8) | (data_buf[1]); | |||||
return value; | |||||
} | |||||
int readCo2(void) { | |||||
int co2 = 0; | |||||
byte frame[8] = {0}; | |||||
buildFrame(0xFE, 0x04, 0x03, 1, frame); | |||||
co2 = sendCommand(frame); | |||||
return co2; | |||||
} | |||||
private: | |||||
// Compute the MODBUS RTU CRC | |||||
static unsigned int modRTU_CRC(byte buf[], int len, byte checkSum[2]) { | |||||
unsigned int crc = 0xFFFF; | |||||
for (int pos = 0; pos < len; pos++) { | |||||
crc ^= (unsigned int)buf[pos]; // XOR byte into least sig. byte of crc | |||||
for (int i = 8; i != 0; i--) { // Loop over each bit | |||||
if ((crc & 0x0001) != 0) { // If the LSB is set | |||||
crc >>= 1; // Shift right and XOR 0xA001 | |||||
crc ^= 0xA001; | |||||
} | |||||
else // Else LSB is not set | |||||
crc >>= 1; // Just shift right | |||||
} | |||||
} | |||||
// Note, this number has low and high bytes swapped, so use it accordingly (or swap bytes) | |||||
checkSum[1] = (byte)((crc >> 8) & 0xFF); | |||||
checkSum[0] = (byte)(crc & 0xFF); | |||||
return crc; | |||||
} | |||||
static int getBitOfInt(int reg, int pos) { | |||||
// Create a mask | |||||
int mask = 0x01 << pos; | |||||
// Mask the status register | |||||
int masked_register = mask & reg; | |||||
// Shift the result of masked register back to position 0 | |||||
int result = masked_register >> pos; | |||||
return result; | |||||
} | |||||
static void buildFrame(byte slaveAddress, | |||||
byte functionCode, | |||||
short startAddress, | |||||
short numberOfRegisters, | |||||
byte frame[8]) { | |||||
frame[0] = slaveAddress; | |||||
frame[1] = functionCode; | |||||
frame[2] = (byte)(startAddress >> 8); | |||||
frame[3] = (byte)(startAddress); | |||||
frame[4] = (byte)(numberOfRegisters >> 8); | |||||
frame[5] = (byte)(numberOfRegisters); | |||||
// CRC-calculation | |||||
byte checkSum[2] = {0}; | |||||
modRTU_CRC(frame, 6, checkSum); | |||||
frame[6] = checkSum[0]; | |||||
frame[7] = checkSum[1]; | |||||
} | |||||
}; | |||||
// | |||||
class SenseAirSensor : public BaseSensor, SenseAir { | |||||
public: | |||||
// --------------------------------------------------------------------- | |||||
// Public | |||||
// --------------------------------------------------------------------- | |||||
SenseAirSensor(): BaseSensor() { | |||||
_count = 1; | |||||
_co2 = 0; | |||||
_lastCo2 = 0; | |||||
_serial = NULL; | |||||
_sensor_id = SENSOR_SENSEAIR_ID; | |||||
} | |||||
~SenseAirSensor() { | |||||
if (_serial) delete _serial; | |||||
_serial = NULL; | |||||
} | |||||
void setRX(unsigned char pin_rx) { | |||||
if (_pin_rx == pin_rx) return; | |||||
_pin_rx = pin_rx; | |||||
_dirty = true; | |||||
} | |||||
void setTX(unsigned char pin_tx) { | |||||
if (_pin_tx == pin_tx) return; | |||||
_pin_tx = pin_tx; | |||||
_dirty = true; | |||||
} | |||||
// --------------------------------------------------------------------- | |||||
unsigned char getRX() { | |||||
return _pin_rx; | |||||
} | |||||
unsigned char getTX() { | |||||
return _pin_tx; | |||||
} | |||||
// --------------------------------------------------------------------- | |||||
// Sensor API | |||||
// --------------------------------------------------------------------- | |||||
// Initialization method, must be idempotent | |||||
void begin() { | |||||
if (!_dirty) return; | |||||
if (_serial) delete _serial; | |||||
_serial = new SoftwareSerial(_pin_rx, _pin_tx, false, 64); | |||||
_serial->enableIntTx(false); | |||||
_serial->begin(9600); | |||||
_serial->enableRx(true); | |||||
_startTime = 0; | |||||
_ready = true; | |||||
_dirty = false; | |||||
} | |||||
// Descriptive name of the sensor | |||||
String description() { | |||||
char buffer[28]; | |||||
snprintf(buffer, sizeof(buffer), "SenseAir S8 @ SwSerial(%u,%u)", _pin_rx, _pin_tx); | |||||
return String(buffer); | |||||
} | |||||
// Descriptive name of the slot # index | |||||
String slot(unsigned char index) { | |||||
return description(); | |||||
} | |||||
// Address of the sensor (it could be the GPIO or I2C address) | |||||
String address(unsigned char index) { | |||||
char buffer[6]; | |||||
snprintf(buffer, sizeof(buffer), "%u:%u", _pin_rx, _pin_tx); | |||||
return String(buffer); | |||||
} | |||||
// Type for slot # index | |||||
unsigned char type(unsigned char index) { | |||||
return MAGNITUDE_CO2; | |||||
} | |||||
void pre() { | |||||
if (millis() - _startTime < 20000) { | |||||
_error = SENSOR_ERROR_WARM_UP; | |||||
return; | |||||
} | |||||
_error = SENSOR_ERROR_OK; | |||||
unsigned int co2 = readCo2(); | |||||
if (co2 >= 5000 || co2 < 100) | |||||
{ | |||||
_co2 = _lastCo2; | |||||
} | |||||
else | |||||
{ | |||||
_co2 = (co2 > _lastCo2 + 2000) ? _lastCo2 : co2; | |||||
_lastCo2 = co2; | |||||
} | |||||
} | |||||
// Current value for slot # index | |||||
double value(unsigned char index) { | |||||
return _co2; | |||||
} | |||||
protected: | |||||
unsigned int _pin_rx; | |||||
unsigned int _pin_tx; | |||||
unsigned long _startTime; | |||||
unsigned int _co2; | |||||
unsigned int _lastCo2; | |||||
}; | |||||
#endif // SENSOR_SUPPORT && SENSEAIR_SUPPORT |
@ -0,0 +1,94 @@ | |||||
// ----------------------------------------------------------------------------- | |||||
// TMP3X Temperature Analog Sensor | |||||
// Copyright (C) 2018 by Xose Pérez <xose dot perez at gmail dot com> | |||||
// ----------------------------------------------------------------------------- | |||||
#if SENSOR_SUPPORT && TMP3X_SUPPORT | |||||
#pragma once | |||||
// Set ADC to TOUT pin | |||||
#undef ADC_MODE_VALUE | |||||
#define ADC_MODE_VALUE ADC_TOUT | |||||
#include "Arduino.h" | |||||
#include "BaseSensor.h" | |||||
#define TMP3X_TMP35 35 | |||||
#define TMP3X_TMP36 36 | |||||
#define TMP3X_TMP37 37 | |||||
class TMP3XSensor : public BaseSensor { | |||||
public: | |||||
// --------------------------------------------------------------------- | |||||
// Public | |||||
// --------------------------------------------------------------------- | |||||
TMP3XSensor(): BaseSensor() { | |||||
_count = 1; | |||||
_sensor_id = SENSOR_TMP3X_ID; | |||||
} | |||||
void setType(unsigned char type) { | |||||
if (35 <= type && type <= 37) { | |||||
_type = type; | |||||
} | |||||
} | |||||
unsigned char getType() { | |||||
return _type; | |||||
} | |||||
// --------------------------------------------------------------------- | |||||
// Sensor API | |||||
// --------------------------------------------------------------------- | |||||
// Initialization method, must be idempotent | |||||
void begin() { | |||||
pinMode(0, INPUT); | |||||
_ready = true; | |||||
} | |||||
// Descriptive name of the sensor | |||||
String description() { | |||||
char buffer[14]; | |||||
snprintf(buffer, sizeof(buffer), "TMP%d @ TOUT", _type); | |||||
return String(buffer); | |||||
} | |||||
// Descriptive name of the slot # index | |||||
String slot(unsigned char index) { | |||||
return description(); | |||||
}; | |||||
// Address of the sensor (it could be the GPIO or I2C address) | |||||
String address(unsigned char index) { | |||||
return String("0"); | |||||
} | |||||
// Type for slot # index | |||||
unsigned char type(unsigned char index) { | |||||
if (index == 0) return MAGNITUDE_TEMPERATURE; | |||||
return MAGNITUDE_NONE; | |||||
} | |||||
// Current value for slot # index | |||||
double value(unsigned char index) { | |||||
if (index == 0) { | |||||
double mV = 3300.0 * analogRead(0) / 1024.0; | |||||
if (_type == TMP3X_TMP35) return mV / 10.0; | |||||
if (_type == TMP3X_TMP36) return mV / 10.0 - 50.0; | |||||
if (_type == TMP3X_TMP37) return mV / 20.0; | |||||
} | |||||
return 0; | |||||
} | |||||
private: | |||||
unsigned char _type = TMP3X_TMP35; | |||||
}; | |||||
#endif // SENSOR_SUPPORT && TMP3X_SUPPORT |
@ -0,0 +1,105 @@ | |||||
/* | |||||
UART_MQTT MODULE | |||||
Copyright (C) 2018 by Albert Weterings | |||||
Adapted by Xose Pérez <xose dot perez at gmail dot com> | |||||
*/ | |||||
#if UART_MQTT_SUPPORT | |||||
char _uartmqttBuffer[UART_MQTT_BUFFER_SIZE]; | |||||
bool _uartmqttNewData = false; | |||||
#if UART_MQTT_USE_SOFT | |||||
#include <SoftwareSerial.h> | |||||
SoftwareSerial _uart_mqtt_serial(UART_MQTT_RX_PIN, UART_MQTT_TX_PIN, false, UART_MQTT_BUFFER_SIZE); | |||||
#define UART_MQTT_PORT _uart_mqtt_serial | |||||
#else | |||||
#define UART_MQTT_PORT UART_MQTT_HW_PORT | |||||
#endif | |||||
// ----------------------------------------------------------------------------- | |||||
// Private | |||||
// ----------------------------------------------------------------------------- | |||||
void _uartmqttReceiveUART() { | |||||
static unsigned char ndx = 0; | |||||
while (UART_MQTT_PORT.available() > 0 && _uartmqttNewData == false) { | |||||
char rc = UART_MQTT_PORT.read(); | |||||
if (rc != UART_MQTT_TERMINATION) { | |||||
_uartmqttBuffer[ndx] = rc; | |||||
if (ndx < UART_MQTT_BUFFER_SIZE - 1) ndx++; | |||||
} else { | |||||
_uartmqttBuffer[ndx] = '\0'; | |||||
_uartmqttNewData = true; | |||||
ndx = 0; | |||||
} | |||||
} | |||||
} | |||||
void _uartmqttSendMQTT() { | |||||
if (_uartmqttNewData == true && MQTT_SUPPORT) { | |||||
DEBUG_MSG_P(PSTR("[UART_MQTT] Send data over MQTT: %s\n"), _uartmqttBuffer); | |||||
mqttSend(MQTT_TOPIC_UARTIN, _uartmqttBuffer); | |||||
_uartmqttNewData = false; | |||||
} | |||||
} | |||||
void _uartmqttSendUART(const char * message) { | |||||
DEBUG_MSG_P(PSTR("[UART_MQTT] Send data over UART: %s\n"), message); | |||||
UART_MQTT_PORT.println(message); | |||||
} | |||||
void _uartmqttMQTTCallback(unsigned int type, const char * topic, const char * payload) { | |||||
if (type == MQTT_CONNECT_EVENT) { | |||||
mqttSubscribe(MQTT_TOPIC_UARTOUT); | |||||
} | |||||
if (type == MQTT_MESSAGE_EVENT) { | |||||
// Match topic | |||||
String t = mqttMagnitude((char *) topic); | |||||
if (t.equals(MQTT_TOPIC_UARTOUT)) { | |||||
_uartmqttSendUART(payload); | |||||
} | |||||
} | |||||
} | |||||
// ----------------------------------------------------------------------------- | |||||
// SETUP & LOOP | |||||
// ----------------------------------------------------------------------------- | |||||
void _uartmqttLoop() { | |||||
_uartmqttReceiveUART(); | |||||
_uartmqttSendMQTT(); | |||||
} | |||||
void uartmqttSetup() { | |||||
// Init port | |||||
UART_MQTT_PORT.begin(UART_MQTT_BAUDRATE); | |||||
// Register MQTT callbackj | |||||
mqttRegister(_uartmqttMQTTCallback); | |||||
// Register loop | |||||
espurnaRegisterLoop(_uartmqttLoop); | |||||
} | |||||
#endif // UART_MQTT_SUPPORT |