準備
コンパイル
この世界ではソースコードと言わずにスケッチというそうですHi。
このままでは動作しません。個別のハードウェア環境、APRS環境、ネットワーク環境に合わせたスケッチの修正が必要です
スケッチの最初の部分に個別設定がありますので、その部分を各自の環境に合わせて修正を行ってください。
最初に #include "jf6lze-13.h"などの部分をコメントアウトしてください。私の個人的設定を読み込むようにしていますが、これは不要です。リリースタイミングによっては jg6ycl-13.h, jg6ycl-15.hを読み込むようにしているかもしれません。
/* -*- c -*- */
/*
Temperature, Humidity, Pressure Web Server (JSON) and APRS Weather Station
for ESP32 (ESP32-WROOM-32)
Copyright (C) 2019,2020,2021 by JF6LZE. All Rights Reserved.
鐚
Multitask Version.. (2019.09.20-)
$Id: esp32_bme280_webserver.ino,v 1.4 2023/11/18 06:36:03 yahiro Exp $
*/
/*
* save to esp32-wx/public/
*/
const char * ktwx_version = "0.08.2";
/*
https://github.com/mgo-tec/ESP32_BME280_I2C
https://github.com/mgo-tec/ESP32_BME280_I2C/archive/master.zip
*/
#include <ESP32_BME280_I2C.h>
/*
Copyright (c) 2015, Majenko Technologies
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
* * Neither the name of Majenko Technologies nor the names of its
contributors may be used to endorse or promote product derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
//#define USE_ARDUINO
#include <WiFi.h>
#include <WiFiMulti.h>
#include <WiFiClient.h>
#ifdef USE_ARDUINO
#include <Ethernet.h>
#else
#include <WebServer.h>
#include <HTTP_Method.h>
#include <ESPmDNS.h>
#endif
#include <time.h>
/*
https://github.com/espressif/arduino-esp32
*/
// *************************
// Configureation Section
//
// 潟潟ゃ絆邃梧┛include祿磧祿磚礒祟筰竍┏荐絎茵磽礒
//
// *************************
// for TEST Configuration
//#include "jf6lze-13.h" // ESP32 DevKit SS
//#include "jg6ycl-13.h" // ESP32 DevKit Waves
//#include "jg6ycl-15.h" // M5Stack
//#include "jg6ycl-14.h" // 弱 ESP32 NodeE-MCU32
//#include "studyroom.h" // 弱 ESP32 NodeE-MCU32
#include "dining_m5stack.h" // M5Stack NO APRS (with Network, Web Server)
//
// WiFi configuration (2.4GHz)
//
#ifndef DEF_CONF_WIFI
#define DEF_CONF_WIFI
const char *ssid = "AP-SSID";
const char * password = "secret-key";
#endif
/*
IP configuration
DHCPP≪禮礫礬竇彰define USE_DHCP
阪IP經絲竢define USE_DHCP 潟<潟≪礒祟筮羆竇臂礒
*/
//#define USE_DHCP
#if !defined( USE_DHCP ) && !defined( DEF_CONF_IP )
#define DEF_CONF_IP
IPAddress local_IP(192, 168, 1, 59);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);
IPAddress primaryDNS(8, 8, 8, 8);
IPAddress secondaryDNS(8, 8, 4, 4);
#endif
WiFiMulti wifiMulti;
//
// for APRS-IS
// for Ham Radio
// APRS-IS肆院羆邃拭禊絲#define USE_APRS鴻礒
//
//#define USE_APRS
#if defined( USE_APRS ) && !defined( DEF_CONF_APRS )
#define DEF_CONF_APRS
const char * callsign_ssid = "NOCALL-13"; // 潟若禝磴SSID綽
const int passcode = 00000; // APRS-IS磚祚禹禮asscode
//
// 祉潟泣絎ゅ荐臀禊絎聿県礙羚水墾APRS-IS蜆拭礒秕祿秕祟
// 經絲邃梧┏閻ぐ 0 礒
// 乗PRSс竊県綺羚水墾腥冴若帥礙祚粭т硯礫秕
const int send_humidity = 1;
const int send_temperature = 1;
// Your Location
const float lat = 33.0000; // 膩綺
const float lng = 130.0000; // 腟綺
const float altitude = 75; // altitude [m] // 罔蕭 [m] 鐚羂с蒿g
// Upstream APRS-IS
const char * host = "wave.ftokai-u.ac.jp"; // Old-T2FUKUOKA or fukuoka.aprs2.net
const uint16_t port = 14580;
const int aprs_update_interval = 5; // 贋育 []
#endif
//
// WebServer罘純鴻禊 #define USE_WEBSERVER 鴻礒
//
//#define USE_WEBSERVER
//
//
//
//#define USE_DEEP_SLEEP
//
//
//
// ESP32 and MBE280 Hardware configuration
//
#ifndef DEF_CONF_GPIO
#define DEF_CONF_GPIO
const uint8_t Address = 0x76;
//const uint8_t Address = 0x77;
//
// BME280 SDA, SCLョGPIO絋經礒
// ョGPIO絋經礒
//
// 腮箝莉PIO (ESP32)
// 6,7,8,9,10,11,15 箍禹禹
// M5Stack經繙<PIO紲礑紿腮礒礬腮礑秕
// 0,1,3,18,19,23,25
// LCD≫ 14,18,23,27,33
// 34-39 INPUT
// M5Stack經綮祿禝禺礙礒https://amzn.to/34NHbXi 篏帥翫邃梧┴禮禮
// BME2805Stack礑磚礒竢M5Stack膠祟綵運礒竇幻祕綺羝綺Γ礒蒿謙礒秕秕
// 鐚Grove SDA, SCLャ帥荐紮Grove篏窮禊絲羈鐚
// const uint8_t sda = 22;
// const uint8_t scl = 21;
// 箝竏君絣竏申磽礒
// https://github.com/m5stack/M5-Schematic/blob/master/Core/Basic/M5-Core-Schematic(20171206).pdf
const uint8_t sda = 21;
const uint8_t scl = 22;
//const uint8_t sda = 32;
//const uint8_t scl = 33;
//
// LED configureation
// LEDョ禊絲0絎
// M5Stack經3紿腮
const uint8_t busy_led = 0;
#endif
#if !defined( DEF_CONF_USE_ANALOG )
#define DEF_CONF_USE_ANALOG
//
// ≪祕禹祿禝礇膣絲#define USE_ANALOG 絎臂礒
//
#define USE_ANALOG
const int analog_gpio[] = { 34, 35 };
#endif
#if !defined( DEF_CONF_MORSE_LED )
#define DEF_CONF_MORSE_LED
//
// 羂羝礫礬磚禺祀禮篆〃LED鐚ч
#define USE_MORSE_LED
const uint8_t morse_led = busy_led;
const uint8_t morse_speed = 200; // 医ゃ絨薨秕
#endif
//
// M5Stack腮禊絲USE_M5STACK絎臂ESP32冴若經絲祿磧祿磚礒
//#define USE_M5STACK
#define LCD_BRIGHTNESS 150
#define USE_M5STACK_POWER_CONTROL
#if !defined( DEF_CONF_USE_LCD_SSD1306 )
#define DEF_CONF_USE_LCD_SSD1306
//
//
// https://amzn.to/2ntemy9 篏帥翫邃梧┨祀礒竢執邃賢
//#define USE_LCD_SSD1306
//
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
#endif
#if !defined( DEF_CONF_USE_MQTT )
#define DEF_CONF_USE_MQTT
// MQTT肆院禺秧邃拭禊絲USE_MQTT絎臂礒
// 磴礬礫禺祚https://github.com/knolleary/pubsubclient
//
#define USE_MQTT
const char * mqtt_host = "ketaitracker.info";
const int mqtt_port = 1883;
const char * mqtt_topic_prifix = "aprs/wx";
char mqtt_topic[256]; // = "aprs/wx/dummy"; // test now
#endif
#ifndef DEF_CONF_USE_OTA
#define DEF_CONF_USE_OTA
//
// OTA腮禊define USE_OTA鴻礒
// Bluetooth腟宴с祕礬祟羇羂礑祀礫秕
#define USE_OTA
#endif
/////////////////////////////
//
// 篁ヤ紊眼繖梧Η
//
/////////////////////////////
#ifdef USE_OTA
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#endif
#if defined( USE_APRS )
const char * mdns_name = callsign_ssid;
#else
const char * mdns_name = "esp32-wx";
#endif
#ifdef USE_DEEP_SLEEP
#undef USE_WEBSERVER
#endif
#ifdef USE_M5STACK
#include <M5Stack.h>
#endif
long timezone = 0;
byte daysavetime = 0;
#ifdef USE_WEBSERVER
#ifdef USE_ARDUINO
EthernetServer server(80);
#else
WebServer server(80);
#endif
#endif
#ifdef USE_APRS
WiFiClient aprs;
#endif
#ifdef USE_MQTT
//
// сMQTT_MAX_PACKET_SIZE 紊眼礬磴礬礫禺祀綵運秕
// ~/Document/Arduino/libraries/pubsubclient-master/src/PubSubClient.h
// #define MQTT_MAX_PACKET_SIZE 256 經礒
//#define MQTT_MAX_PACKET_SIZE 256
#include <PubSubClient.h>
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
#endif
#define STATUS_BUSY 1
#define STATUS_IDLE 0
//
// LCD SSD1306
#if defined( USE_LCD_SSD1306 )
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#ifndef SCREEN_WIDTH
#define SCREEN_WIDTH 128
#endif
#ifndef SCREEN_HEIGHT
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
#endif
#define USE_LCD
#define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#endif // end if USE_LCD_SSD1306
#if defined( USE_M5STACK )
#undef USE_LCD
#endif
//const int led = 13;
const uint32_t frequency = 30000;
ESP32_BME280_I2C bme280i2c(Address, scl, sda, frequency);
//
// mean-sea-level barometric pressure
//
double adjustPressure( double pressure, double temperature, double altitude )
{
return pressure * pow( 1 - 0.0056 * altitude / ( temperature + 0.0065 * altitude + 273.15), 5.257 );
}
void setup(void) {
unsigned long system_start_time = millis();
unsigned long system_ready_time;
Serial.begin(115200);
Serial.println( );
#if defined( __AVR__ )
Serial.println( "ARDUINO Enabled" );
#endif
#if defined( ESP32 )
Serial.println( "ESP32 Enabled" );
#endif
#if defined( ESP8266 )
Serial.printlmn( "ESP8266 Enabled" );
#endif
#if defined( USE_LCD ) || defined( USE_LCD_SSD1306 )
Serial.println( "LCD_SSD1306 Enabled" );
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32
Serial.println(F("SSD1306 allocation failed"));
for (;;); // Don't proceed, loop forever
}
display.display();
display.clearDisplay();
display.setTextSize( 1 );
display.setTextColor( WHITE );
display.setCursor( 0, 0 );
display.printf( "%s", "KetaiTracker ESP" );
display.setCursor( 0, 10 );
display.setTextSize( 2 );
display.printf( "%s", ktwx_version );
display.display();
#endif
#ifdef USE_M5STACK
if ( sda == 25 || scl == 25 || busy_led == 25 ) {
// M5Stack5綺紲綉禮禺祀禺祟礑蒿絵.
Serial.println( "Error: Don't use GPIO 25" );
}
#endif
#ifndef USE_ARDUINO
pinMode( sda, PULLUP );
pinMode( scl, PULLUP );
#endif
if ( busy_led != 0 )
pinMode( busy_led, OUTPUT );
#ifdef USE_MORSE_LED
if ( morse_led != 0 )
pinMode( morse_led, OUTPUT );
#endif
Serial.println( "" );
#ifdef USE_M5STACK
Serial.println( "M5STACK" );
M5.begin();
//M5.Speaker.begin();
//M5.Speaker.end();
M5.Lcd.println( "KetaiTracker WX" );
#ifdef USE_M5STACK_POWER_CONTROL
if ( M5.Power.canControl()) {
M5.Lcd.println( "Contorable Power" );
if ( M5.Power.isChargeFull()) {
M5.Lcd.print( "Battery: full" );
} else {
M5.Lcd.print( "Battery: NOT full" );
}
M5.Lcd.printf( ",%d%% ", M5.Power.getBatteryLevel());
if ( M5.Power.isCharging()) {
M5.Lcd.println( ", Charging Now" );
} else {
M5.Lcd.println( "" );
}
} else {
M5.Lcd.println( "Power Control is disbaled (May be.. Old M5Stack)" );
}
#endif
#ifdef USE_APRS
M5.Lcd.printf( "Callsign: %s\n", callsign_ssid );
#endif
#endif
#ifdef USE_MQTT
sprintf( mqtt_topic, "%s/%s", mqtt_topic_prifix, callsign_ssid );
Serial.printf( "MQTT publish enabled %s:%d, topic: %s\n", mqtt_host, mqtt_port, mqtt_topic );
Serial.printf( "MQTT maximum message size is %d, keepalive interval is %d\n", MQTT_MAX_PACKET_SIZE, MQTT_KEEPALIVE );
#endif
statusLed( STATUS_BUSY );
#ifdef USE_MORSE_LED
Serial.println( "Morse LED Enabled" );
#endif
#ifdef USE_WEBSERVER
Serial.println( "Web Server Enabled" );
#endif
#ifdef USE_APRS
Serial.println( "APRS Weather Station Enbaled" );
#endif
#ifdef USE_ANALOG
Serial.println( "Analog Input Enabled" );
#endif
#ifdef USE_DHCP
Serial.println( 'use DHCP Server' );
#else
Serial.println( 'use Static IP Address' );
if ( !WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS)) {
Serial.println("STA Failed to configure");
}
#endif
#if 1
initWiFi();
#else
WiFi.mode( WIFI_STA );
WiFi.begin( ssid, password);
#endif
Serial.println("");
/*
Serial.print( "Reconned mode is " );
Serial.println( WiFi.getAutoReconnect() );
*/
// Wait for connection
#if 0
keepWifi();
Serial.println( "" );
Serial.print( "Connected to " );
Serial.println(ssid);
Serial.print( "IP address: " );
Serial.println( WiFi.localIP() );
#endif
#ifdef USE_M5STACK
M5.Lcd.print( "IP: " );
M5.Lcd.println( WiFi.localIP());
#endif
if ( MDNS.begin( mdns_name )) {
Serial.printf("MDNS responder started : %s.local\n", mdns_name );
}
// BME280
//Example Indoor navigation
uint8_t t_sb = 0; //stanby 0.5ms
uint8_t filter = 4; //IIR filter = 16
uint8_t osrs_t = 2; //OverSampling Temperature x2
uint8_t osrs_p = 5; //OverSampling Pressure x16
uint8_t osrs_h = 1; //OverSampling Humidity x1
uint8_t Mode = 3; //Normal mode
bme280i2c.ESP32_BME280_I2C_Init(t_sb, filter, osrs_t, osrs_p, osrs_h, Mode);
Wire.begin();
// I2C check
if ( 1 ) {
Wire.beginTransmission( Address );
byte error = Wire.endTransmission();
if ( error != 0 ) {
#ifdef USE_M5STACK
M5.Lcd.printf( "Error: Not found BME280 at 0x%x\n", Address );
#endif
Serial.printf( "Error: Not found BME280 at 0x%x\n", Address );
msgError();
}
}
syncNtp();
struct tm tmstruct ;
// delay(2000);
tmstruct.tm_year = 0;
getLocalTime(&tmstruct, 5000);
Serial.printf("Now %d-%02d-%02d %02d:%02d:%02d\n",
(tmstruct.tm_year) + 1900,
( tmstruct.tm_mon) + 1,
tmstruct.tm_mday,
tmstruct.tm_hour ,
tmstruct.tm_min,
tmstruct.tm_sec
);
Serial.println("");
#ifdef USE_M5STACK
M5.Lcd.printf( "Time: %d-%02d-%02d %02d:%02d:%02d\n",
(tmstruct.tm_year) + 1900,
( tmstruct.tm_mon) + 1,
tmstruct.tm_mday,
tmstruct.tm_hour ,
tmstruct.tm_min,
tmstruct.tm_sec
);
#endif
#ifdef USE_WEBSERVER
server.on( "/", putBme280Json);
server.on( "/bme", putBme280Json );
server.onNotFound(handleNotFound);
server.begin();
Serial.println("HTTP server started");
#ifdef M5STACK
M5.Lcd.println( "HTTP Server Started" );
#endif
#endif
statusLed( STATUS_IDLE );
#if defined( USE_M5STACK ) || defined( USE_LCD )
lcdWakeup();
// M5.Lcd.println( "" );
initLcd();
// 篁ラM5.Lcd.print激禺禳祚紿腮礫
// dispLcd( x, y, size, fmt, ... );
// dispLcdColor( x, y, size, color, fmt, ... );
#endif
#if defined( USE_OTA )
initOta();
#endif
Serial.print( "Setup() Core is is " );
Serial.println( xPortGetCoreID() );
#if defined( USE_MQTT )
//connectMqtt();
#endif
xTaskCreatePinnedToCore(taskSystem, "taskSystem", 8192, NULL, 10 /* Priolity */ , NULL, 1 /* Core ID */);
#if defined( USE_WEBSERVER ) && !defined( USE_DEEP_SLEEP )
xTaskCreatePinnedToCore(taskWebServer, "taskWebServer", 8192, NULL, 5 /* Priolity */ , NULL, 1 /* Core ID */);
#endif
#if defined( USE_APRS ) && !defined( USE_DEEP_SLEEP )
xTaskCreatePinnedToCore(taskAprs, "taskAprs", 8192, NULL, 20 /* Priolity */ , NULL, 1 /* Core ID */);
#endif
#if defined( USE_MORSE_LED )
if ( morse_led > 0 ) {
#if defined( USE_APRS )
setMorse( callsign_ssid );
#endif
xTaskCreatePinnedToCore(taskMorseLed, "taskMorseLed", 8192, NULL, 10 /* Priolity */ , NULL, 1 /* Core ID */);
}
#endif
#if defined( USE_MQTT )
//xTaskCreatePinnedToCore(taskMqtt, "taskMqtt", 8192, NULL, 40 /* Priolity */ , NULL, 1 /* Core ID */);
#endif
#ifdef USE_DEEP_SLEEP
send_aprs();
delay(2000);
aprs.stop();
Serial.println( "Goto Deep Sleep" );
esp_deep_sleep( aprs_update_interval * 60000000 - ( millis() - system_start_time ) * 1000 );
#else
delay(1000);
#endif
}
void loop(void) {
#ifndef USE_DEEP_SLEEP
keepWifi();
#endif
delay( 10000 );
//Serial.println( "loop()" );
}
void initWiFi()
{
int n;
char * ssid, * password;
char buf[256];
n = sizeof( ap_list ) / sizeof( char * );
//USE_SERIAL.println( n );
for ( int i = 0 ; i < n ; i++ ) {
sprintf( buf, "%s", ap_list[i] );
ssid = strtok( buf, "," );
password = strtok( NULL, "," );
wifiMulti.addAP( ssid, password );
}
WiFi.config( local_IP, gateway, subnet, primaryDNS );
Serial.println("Connecting Wifi...");
if (wifiMulti.run() == WL_CONNECTED) {
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
}
int readThpAvg( double * t, double * p, double * h )
{
double temperature = 0.0, humidity = 0.0, pressure = 0.0;
double t_t, t_h, t_p ;
int c = 0 ;
int n = 30; // 緇
int i;
for ( i = 0 ; i < n ; i++ ) {
c = 0;
do {
bme280i2c.Read_All( &t_t, &t_p, &t_h);
delay( 10 );
c++;
if ( c > 40 ) {
ESP.restart();
}
} while ( t_h == 0 || t_h == 100 );
temperature += t_t;
humidity += t_h;
pressure += t_p;
}
* t = temperature / n;
* p = pressure / n;
* h = humidity / n;
return 0;
}
int readThp( double * t, double * p, double * h )
{
double temperature, humidity, pressure;
int c = 0 ;
do {
bme280i2c.Read_All( &temperature, &pressure, &humidity);
delay( 250 );
c++;
if ( c > 40 ) {
ESP.restart();
}
} while ( humidity == 0 || humidity == 100 );
* t = temperature;
* p = pressure;
* h = humidity;
return 0;
}
void msgError()
{
int cnt = 0;
Serial.println( "abort system due to ERROR" );
#ifdef USE_M5STACK
M5.Lcd.printf( "Abort system due to error\n" );
while ( 1 ) {
delay( 1000 );
}
#else
while ( 1 ) {
if ( busy_led != 0 ) {
digitalWrite( busy_led, HIGH );
if ( morse_led != 0 && morse_led != busy_led )
digitalWrite( morse_led, LOW );
delay( 50 );
digitalWrite( busy_led, LOW );
if ( morse_led != 0 && morse_led != busy_led )
digitalWrite( morse_led, HIGH );
delay( 50 );
}
cnt++;
if ( cnt > 100 )
ESP.restart();
}
#endif
}
//
// System Loop Task
// display Clock and Tenperature, Humidity
void taskSystem( void * arg )
{
static time_t nextMorse = 0;
static boolean setNextMorse = true;
Serial.print( "taskSystem() Core is is " );
Serial.println( xPortGetCoreID() );
while ( 1 ) {
//Serial.println( "taskSystem() loop" );
#if defined( USE_M5STACK )
M5.update();
if ( M5.BtnA.isPressed() ) {
lcdWakeup();
}
if ( M5.BtnC.isPressed() ) {
lcdSleep();
}
dispStatus();
lcdAutoSleep();
#endif
#if defined( USE_LCD )
dispStatus();
#endif
#if defined( USE_MORSE_LED )
if (setNextMorse && now() > nextMorse ) {
setNextMorse = false;
statusMorse();
}
if ( !setNextMorse && getMorseQueN() == 0 ) {
// Serial.println( "set NextMorse time" );
setNextMorse = true;
nextMorse = now() + 30;
}
#endif
#if defined( USE_OTA )
ArduinoOTA.handle();
#endif
delay( 100 );
}
}
#if defined( USE_WEBSERVER )
//
// WebServer Task
//
void taskWebServer( void * arg )
{
Serial.print( "taskWebServer() Core is is " );
Serial.println( xPortGetCoreID() );
while ( 1 ) {
server.handleClient();
delay( 30 );
}
}
#endif
#if defined( USE_APRS )
//
// APRS≫秧禮
//
void taskAprs( void * arg )
{
static time_t packet_send_time = 0;
int c;
Serial.print( "taskAprs() Core is is " );
Serial.println( xPortGetCoreID() );
while ( 1 ) {
while ( aprs.available()) {
c = aprs.read();
// Serial.print( aprs.read());
}
//Serial.println( "taskAprs() loop" );
if ( now() > packet_send_time ) {
packet_send_time = now() + 60 * aprs_update_interval; // 5min
send_aprs();
}
delay( 1000 );
}
}
#endif
#if defined( USE_MQTT )
void taskMqtt( void * arg )
{
static time_t send_time = 0;
delay( 1500 );
while ( 1 ) {
if ( now() > send_time ) {
send_time = now() + 29; /* 30 sec interval */
// delay( 1000 );
sendMqtt();
}
delay( 1000 );
}
}
boolean sendMqtt()
{
double temperature, humidity, pressure;
char buf[256];
readThpAvg( &temperature, &pressure, &humidity);
if ( pressure == 0 ) {
Serial.println( "taskMqtt(): illegal data (humidity is zero)" );
} else {
//sprintf( buf, "{\"callsign\":\"%s\",\"temperature\":%.2lf,\"humidity\":%.2lf,\"pressure\":%.2lf,\"time\":%ld}", callsign_ssid, temperature, humidity, pressure, now() );
sprintf( buf, "{\"callsign\":\"%s\",\"temperature\":%lf,\"humidity\":%.lf,\"pressure\":%lf,\"time\":%ld,\"latitude\":%f,\"longitude\":%f}",
callsign_ssid, temperature, humidity, pressure, now() , lat, lng);
if ( (strlen( buf ) + 2 + MQTT_MAX_HEADER_SIZE + strlen( mqtt_topic )) > MQTT_MAX_PACKET_SIZE ) {
Serial.printf( "MQTT over max packet size (%d) < (%d) packet is %s\n", MQTT_MAX_PACKET_SIZE, strlen( buf ), buf );
return false;
}
Serial.printf( "MQTT sending..size %d.", strlen( buf ) );
keepWifi();
keepMqtt();
if ( mqttClient.publish( mqtt_topic, buf ) == false ) {
Serial.println( "MQTT send fail" );
}
mqttClient.loop();
Serial.println( "..done" );
}
return true;
}
void keepMqtt()
{
if ( !mqttClient.connected())
connectMqtt();
}
void connectMqtt()
{
mqttClient.setServer( mqtt_host, mqtt_port );
while ( ! mqttClient.connected() ) {
Serial.printf("Connecting to MQTT...%s:%d ", mqtt_host, mqtt_port );
String clientId = "ESP32-" + String(random(0xffff), HEX);
if ( mqttClient.connect(clientId.c_str()) ) {
Serial.println("connected");
}
delay(1000);
randomSeed(micros());
}
}
#endif
#if defined( USE_MORSE_LED )
void taskMorseLed( void * arg )
{
while ( 1 ) {
doMorseLed();
delay( 330 );
}
}
#endif
//
// NTP泣若祟繹秧竇篋障у
//
void syncNtp()
{
time_t t;
int cnt = 0;
Serial.print("Contacting Time Server");
#ifdef USE_M5STACK
M5.Lcd.println( "Connecting to Time Server..." );
#endif
configTime(3600 * timezone, daysavetime * 3600,
"ntp.jst.mfeed.ad.jp",
"time.google.com",
"ntp.nict.jp"
);
// configTime薈鐚 delay礬礑竍綺紕礒竇粋
do {
time( &t );
Serial.print( t );
Serial.print( ", " );
delay( 1000 );
cnt++;
if ( cnt > 30 ) {
Serial.println( "Reboot due to fail (Sync NTP)" );
ESP.restart();
}
} while ( t < 1000 ); // 禮竊c祚
Serial.println( " done." );
}
time_t now()
{
time_t t;
time( &t );
return t;
}
void statusLed( int sw )
{
static int cnt = 0;
if ( sw > 0 )
cnt++;
else
cnt--;
/*
Serial.print( "statusLed() &c =" );
char buf[30];
sprintf( buf, "%ld", &cnt );
Serial.print( buf );
Serial.print( ", " );
Serial.print( cnt );
Serial.println( "" );
*/
#ifdef USE_M5STACK
// M5.Lcd.fillEllipse( 310, 10, 10, 10, s > 0 ? TFT_RED : TFT_GREEN );
M5.Lcd.setTextSize( 1 );
if ( cnt > 0 ) { /* budy */
M5.Lcd.setTextColor( TFT_WHITE, TFT_RED );
} else {
M5.Lcd.setTextColor( TFT_WHITE, TFT_DARKGREEN );
}
M5.Lcd.setCursor( 290, 0 );
M5.Lcd.print( cnt > 0 ? "Busy" : "Idle" );
M5.Lcd.setTextColor( TFT_WHITE, TFT_BLACK );
#endif
if ( busy_led == 0 )
return;
digitalWrite( busy_led, cnt > 0 ? HIGH : LOW );
}
void keepWifi()
{
#if 1
int retry = 0;
while (WiFi.status() != WL_CONNECTED) {
delay(2000);
wifiMulti.run();
Serial.print( WiFi.status() );
retry++;
if ( retry > 10 ) {
Serial.println( "" );
Serial.println( "Restart system due to fail wifi connection" );
#ifdef USE_M5STACK
M5.Lcd.printf( "Restart System due to fail Wifi connection\n" );
delay( 3000 );
#endif
ESP.restart();
}
}
#else
int retry = 0;
while (WiFi.status() != WL_CONNECTED) {
delay(2000);
WiFi.begin();
Serial.printf(" Wifi. %s ", ssid );
Serial.print( WiFi.status() );
retry++;
if ( retry > 10 ) {
Serial.println( "" );
Serial.println( "Restart system due to fail wifi connection" );
#ifdef USE_M5STACK
M5.Lcd.printf( "Restart System due to fail Wifi connection\n" );
delay( 3000 );
#endif
ESP.restart();
}
}
#endif
}
#ifdef USE_APRS
void send_aprs()
{
String packet;
double temperature, pressure, humidity, pressure_adj;
String msg;
char pos[30];
char timestr[30];
char sendtimestr[30];
struct tm tmstruct ;
#ifdef USE_M5STACK
lcdWakeup();
#endif
statusLed( STATUS_BUSY );
getLocalTime(&tmstruct, 5000);
sprintf( sendtimestr, "%d-%02d-%02d %02d:%02d:%02d",
(tmstruct.tm_year) + 1900,
( tmstruct.tm_mon) + 1,
tmstruct.tm_mday,
tmstruct.tm_hour ,
tmstruct.tm_min,
tmstruct.tm_sec
);
Serial.printf("Now %s\n", sendtimestr );
sprintf( timestr, "%02d%02d%02dz", tmstruct.tm_mday, tmstruct.tm_hour , tmstruct.tm_min );
// connect to aprs-id
if ( !aprs.connected()) {
#ifdef USE_M5STACK
dispLcd( 0, 190, 1, "Connecting to APRS-IS %s:%d", host, port );
#endif
if (!aprs.connect(host, port)) {
Serial.println("connection failed");
statusLed( STATUS_IDLE );
return;
}
Serial.printf( "connected to APRS-IS %s:%d\n", host, port );
#ifdef USE_M5STACK
dispLcd( 0, 190, 1, "Connected to APRS-IS %s:%d", host, port );
#endif
if ( aprs.connected()) {
char cmd[256];
sprintf( cmd, "user %s pass %d vers KetaiTrackerWX %s ESP32+BMP280",
callsign_ssid,
passcode,
ktwx_version
);
aprs.println( cmd );
Serial.println( cmd );
delay( 100 );
while ( aprs.available() == 0 ) {
Serial.print( 'wait.' );
delay( 100 );
}
String line = aprs.readStringUntil('\r');
Serial.print(line);
}
}
readThpAvg(&temperature, &pressure, &humidity);
pressure_adj = adjustPressure( pressure, temperature, altitude );
//Serial.println ( pressure );
//Serial.println ( pressure_adj );
char temp_c[10], hum_c[10], pres_c[10];
if ( send_temperature ) {
sprintf( temp_c, "t%03.0lf", temperature * 9 / 5.0 + 32 );
} else {
sprintf( temp_c, "t..." );
}
if ( send_humidity ) {
sprintf( hum_c, "h%02.0lf", humidity);
} else {
sprintf( hum_c, "h.." );
}
sprintf( pres_c, "b%05.0lf", pressure_adj * 10);
sprintf( pos, "%02d%05.2fN/%03d%05.2fE",
(int)lat,
(lat - (int)lat) * 60.0,
(int)lng,
(lng - (int)lng) * 60.0
);
char p[60];
sprintf( p, "%s>APPT30,TCPIP*:", callsign_ssid );
packet = p;
if ( 0 ) {
packet += "!";
} else {
packet += "@";
packet += timestr;
}
packet += pos;
packet += "_";
// packet += "000 / 000";
packet += ".../...";
//packet += "c...s...g..."; // Wind
//packet += "c000s000g000";
packet += temp_c;
packet += hum_c;
packet += pres_c;
//packet += " ESP32 + BME280";
Serial.println( packet );
#ifdef USE_M5STACK
M5.Lcd.setTextSize( 1 );
M5.Lcd.setCursor( 0, 180 );
M5.Lcd.printf( "%s UTC", sendtimestr );
M5.Lcd.setCursor( 0, 200 );
M5.Lcd.print( "Sending: " );
M5.Lcd.println( packet );
#endif
aprs.println( packet );
if ( 0 ) {
//aprs.flush(); // Disconnect秕秧
delay(1000);
//aprs.stop(); // Disconnect
}
statusLed( STATUS_IDLE );
}
#endif
// end of #USE_APRS
#ifdef USE_ANALOG
char * getAnalogJson()
{
// TODO: 緇ャC++ c純吾眼
// 鴻祕祟秕秕
static char buf[256];
String msg;
int i, n;
n = sizeof( analog_gpio ) / sizeof( int );
msg = "\"analog\":[";
for ( i = 0; i < n ; i++ ) {
if ( i != 0 )
msg += ",";
msg += analogRead( analog_gpio[i] );
}
msg += "]";
snprintf( buf, 256, "%s", msg.c_str() );
//Serial.println( buf );
return buf;
}
int * getAnalog()
{
static int buf[10];
// TODO: 緇ャC++ c純吾眼
// 鴻祕祟秕秕
int i, n;
n = sizeof( analog_gpio ) / sizeof( int );
for ( i = 0; i < n ; i++ ) {
buf[i] = analogRead( analog_gpio[i] );
}
return buf;
}
#endif
#ifdef USE_WEBSERVER
void handleNotFound() {
//digitalWrite(led, 1);
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
server.send( 404, "text/plain", message);
// digitalWrite(led, 0);
}
/*
Web Server
JSON綵√鐚羂с蜈墾蒿c茯
*/
void putBme280Json() {
double temperature, pressure, humidity;
String msg;
char temp_c[10], hum_c[10], pres_c[10];
statusLed( STATUS_BUSY );
server.sendHeader( "Access-Control-Allow-Origin", "*" );
server.sendHeader( "Cache-Control", "no-cache");
readThpAvg(&temperature, &pressure, &humidity);
sprintf(temp_c, "%.2lf", temperature);
sprintf(hum_c, "%.2lf", humidity);
sprintf(pres_c, "%.2lf", pressure);
msg = "{\"temperature\":";
msg += temp_c;
msg += ",\"humidity\":" ;
msg += hum_c;
msg += ",\"pressure\":";
msg += pres_c;
// msg += ",\"dummy\":123";
#ifdef USE_ANALOG
msg += ",";
msg += getAnalogJson();
#endif
msg += ",\"version\":";
msg += "\"";
msg += ktwx_version;
msg += "\"" ;
msg += "}";
//msg += "\n";
server.send( 200, "application/json", msg );
//server.send( 200, "text/plain", msg );
//delay( 500 );
statusLed( STATUS_IDLE );
}
#endif
#if defined( USE_M5STACK ) || defined( USE_LCD )
void initLcd()
{
#if defined( USE_M5STACK )
M5.Lcd.setBrightness( LCD_BRIGHTNESS );
M5.Lcd.clear();
dispLcd( 0, 0, 1, "KetaiTracker WX %s", ktwx_version );
dispLcd( 160, 0, 1, "IP: %s", WiFi.localIP().toString().c_str() );
#ifdef USE_APRS
dispLcd( 0, 10 , 1, "Callsign: %s", callsign_ssid );
dispLcd( 0, 18, 1, "APRS-IS: %s:%d", host, port );
#endif
initGraph();
#endif
#if defined( USE_LCD )
display.clearDisplay();
display.display();
#endif
}
//
//
#ifndef SLEEP_TIME
#define SLEEP_TIME 0
#endif
time_t nextSleepTime = 0;
time_t lcdSleepTime = SLEEP_TIME;
boolean isSleep = false;
void lcdSleep()
{
#if defined( USE_M5STACK )
if ( isSleep )
return;
isSleep = true;
if ( lcdSleepTime > 5 ) {
for ( int i = LCD_BRIGHTNESS; i > 10; i-- ) {
M5.Lcd.setBrightness(i);
delay( 15 );
}
}
M5.Lcd.sleep();
M5.Lcd.setBrightness(0);
#endif
Serial.println( "LCD sleep" );
}
void lcdWakeup()
{
#if defined( USE_M5STACK )
lcdResetSleep();
if ( !isSleep )
return;
isSleep = false;
M5.Lcd.wakeup();
M5.Lcd.setBrightness(LCD_BRIGHTNESS);
Serial.println( "LCD Wakeup" );
#endif
}
void lcdResetSleep()
{
nextSleepTime = now() + lcdSleepTime;
}
void lcdAutoSleep()
{
if ( lcdSleepTime == 0 )
return;
if ( now() > nextSleepTime ) {
lcdSleep();
}
}
void dispLcd( uint16_t x, uint16_t y, uint16_t size, const char * fmt, ... )
{
char buf[1024];
va_list arg;
va_start(arg, fmt );
vsnprintf( buf, 1024, fmt, arg );
#if defined( USE_M5STACK )
M5.Lcd.setCursor( x, y );
M5.Lcd.setTextSize( size );
M5.Lcd.print( buf );
#endif
#if defined( USE_LCD )
display.setCursor( x, y );
display.setTextSize( size );
display.print( buf );
display.display();
#endif
va_end(arg);
}
void dispLcdColor( uint16_t x, uint16_t y, uint16_t size, uint16_t color, const char * fmt, ... )
{
char buf[1024];
va_list arg;
va_start(arg, fmt );
vsnprintf( buf, 1024, fmt, arg );
#if defined( USE_M5STACK )
M5.Lcd.setTextColor( color, TFT_BLACK );
M5.Lcd.setCursor( x, y );
M5.Lcd.setTextSize( size );
M5.Lcd.print( buf );
#endif
#if defined( USE_LCD )
display.setTextColor( color, BLACK );
display.setCursor( x, y );
display.setTextSize( size );
display.print( buf );
display.display();
#endif
va_end(arg);
}
void dispLcdColorBg( uint16_t x, uint16_t y, uint16_t size, uint16_t color, uint16_t bgcolor, const char * fmt, ... )
{
char buf[1024];
va_list arg;
va_start(arg, fmt );
vsnprintf( buf, 1024, fmt, arg );
#if defined( USE_M5STACK )
M5.Lcd.setTextColor( color, bgcolor );
M5.Lcd.setCursor( x, y );
M5.Lcd.setTextSize( size );
M5.Lcd.print( buf );
#endif
#if defined( USE_LCD )
display.setTextColor( color, bgcolor );
display.setCursor( x, y );
display.setTextSize( size );
display.print( buf );
display.display();
#endif
va_end(arg);
}
#if !defined( USE_M5STACK )
#define TFT_RED WHITE
#define TFT_WHITE WHITE
#define TFT_GREEN WHITE
#define TFT_CYAN WHITE
#endif
#define COLOR_TEMPERATURE TFT_RED
#define COLOR_HUMIDITY TFT_CYAN
#define COLOR_PRESSURE TFT_GREEN
#define COLOR_INC TFT_ORANGE
#define COLOR_DEC TFT_CYAN
#define DATA_STACK 600
double calc_avg( double v[], int n )
{
int i;
double t = 0.0;
for ( i = 0 ; i < DATA_STACK && i < n ; i++ ) {
t += v[i];
}
return t / i;
}
void dispStatus()
{
static time_t nextUpdate = 0;
// TODO: 罕篏篏帥c禮祗礫
double temperature, pressure, humidity;
static double temperature_max = -99.0, pressure_max = 0.0, humidity_max = 0.0;
static double temperature_min = 99.9, pressure_min = 2000.0, humidity_min = 100.0;
static double temperature_prev = 0, pressure_prev = 0, humidity_prev = 0;
static double temperature_stack[DATA_STACK], pressure_stack[DATA_STACK], humidity_stack[DATA_STACK];
static int data_stack = 0;
static int data_stack_p ;
uint16_t fg_t, fg_h, fg_p;
uint16_t bg_t_max, bg_t_min, bg_p_max, bg_p_min, bg_h_max, bg_h_min;
int x, y;
if ( nextUpdate > now())
return;
nextUpdate = now() + 1;
// Current time
struct tm tmstruct ;
getLocalTime(&tmstruct, 5000);
/*
M5.Lcd.setCursor( 0, 140 );
M5.Lcd.setTextSize( 1 );
M5.Lcd.printf( "%d-%02d-%02d %02d:%02d:%02d",
(tmstruct.tm_year) + 1900,
( tmstruct.tm_mon) + 1,
tmstruct.tm_mday,
tmstruct.tm_hour ,
tmstruct.tm_min,
tmstruct.tm_sec
);
*/
dispLcdColor( 0, 140, 1, TFT_WHITE, "%d-%02d-%02d %02d:%02d:%02d UTC",
(tmstruct.tm_year) + 1900,
( tmstruct.tm_mon) + 1,
tmstruct.tm_mday,
tmstruct.tm_hour ,
tmstruct.tm_min,
tmstruct.tm_sec);
//
#if defined( USE_LCD )
dispLcdColor( 0, 25, 1, WHITE, "%d/%02d/%02d %02d:%02d:%02d Z",
(tmstruct.tm_year) + 1900,
( tmstruct.tm_mon) + 1,
tmstruct.tm_mday,
tmstruct.tm_hour ,
tmstruct.tm_min,
tmstruct.tm_sec);
#endif
readThp(&temperature, &pressure, &humidity);
/*
M5.Lcd.setCursor( 0, 150 );
M5.Lcd.setTextSize( 2 );
M5.Lcd.printf( "T:%s H:%s P:%s", temp_c, hum_c, pres_c );
*/
//dispLcd( 0, 150, 2, "T:%s H:%s P:%s", temp_c, hum_c, pres_c );
#if defined( USE_M5STACK )
/*
char temp_c[10], hum_c[10], pres_c[10];
sprintf(temp_c, "%5.2lf", temperature);
sprintf(hum_c, "%5.2lf", humidity);
sprintf(pres_c, "%7.2lf", pressure);
*/
/*
サ鐚綛喝
*/
data_stack_p = data_stack % DATA_STACK;
temperature_stack[data_stack_p] = temperature;
humidity_stack[data_stack_p] = humidity;
pressure_stack[data_stack_p] = pressure;
data_stack++;
fg_t = fg_h = fg_p = TFT_WHITE;
if ( data_stack >= DATA_STACK || 1) {
temperature_prev = calc_avg( temperature_stack, data_stack );
humidity_prev = calc_avg( humidity_stack, data_stack );
pressure_prev = calc_avg( pressure_stack, data_stack );
if ( temperature > temperature_prev )
fg_t = COLOR_INC;
if ( temperature < temperature_prev )
fg_t = COLOR_DEC;
if ( humidity > humidity_prev )
fg_h = COLOR_INC;
if ( humidity < humidity_prev )
fg_h = COLOR_DEC;
if ( pressure > pressure_prev )
fg_p = COLOR_INC;
if ( pressure < pressure_prev )
fg_p = COLOR_DEC;
}
y = 150;
x = 20;
dispLcdColor( 0, y, 1, TFT_WHITE, "Now" );
dispLcdColor( x, y, 2, COLOR_TEMPERATURE, "T:" );
dispLcdColor( M5.Lcd.getCursorX(), y, 2, fg_t, "%5.2lf", temperature );
dispLcdColor( M5.Lcd.getCursorX(), y, 2, COLOR_HUMIDITY, " H:" );
dispLcdColor( M5.Lcd.getCursorX(), y, 2, fg_h, "%5.2lf", humidity );
dispLcdColor( M5.Lcd.getCursorX(), y, 2, COLOR_PRESSURE, " P:" );
dispLcdColor( M5.Lcd.getCursorX(), y, 2, fg_p, "%7.2lf", pressure );
/* サ綛喝*/
y = 170;
dispLcdColor( 0, y, 1, TFT_WHITE, "Avg" );
dispLcdColor( x, y, 2, COLOR_TEMPERATURE, "T:" );
dispLcdColor( M5.Lcd.getCursorX(), y, 2, TFT_WHITE, "%5.2lf", temperature_prev );
dispLcdColor( M5.Lcd.getCursorX(), y, 2, COLOR_HUMIDITY, " H:" );
dispLcdColor( M5.Lcd.getCursorX(), y, 2, TFT_WHITE, "%5.2lf", humidity_prev );
dispLcdColor( M5.Lcd.getCursorX(), y, 2, COLOR_PRESSURE, " P:" );
dispLcdColor( M5.Lcd.getCursorX(), y, 2, TFT_WHITE, "%7.2lf", pressure_prev );
if ( setGraphDataInterval( temperature, humidity, pressure )) {
drawGraphAll();
}
/*
蕭篏茵腓削取;腓差禺禮祟蘂PRS腮礫禊絆蜿
*/
#if !defined( USE_APRS )
bg_t_max = bg_t_min = bg_p_max = bg_p_min = bg_h_max = bg_h_min = TFT_BLACK;
if ( temperature_max < temperature ) {
temperature_max = temperature;
bg_t_max = TFT_RED;
}
if ( temperature_min > temperature ) {
temperature_min = temperature;
bg_t_min = TFT_BLUE;
}
if ( humidity_max < humidity ) {
humidity_max = humidity;
bg_h_max = TFT_RED;
}
if ( humidity_min > humidity ) {
humidity_min = humidity;
bg_h_min = TFT_BLUE;
}
if ( pressure_max < pressure ) {
pressure_max = pressure;
bg_p_max = TFT_RED;
}
if ( pressure_min > pressure ) {
pressure_min = pressure;
bg_p_min = TFT_BLUE;
}
y = 200;
dispLcdColor( 0, y, 1, TFT_WHITE, "Max" );
dispLcdColor( x, y, 2, COLOR_TEMPERATURE, "T:" );
dispLcdColorBg( M5.Lcd.getCursorX(), y, 2, TFT_WHITE, bg_t_max, "%5.2lf", temperature_max );
dispLcdColor( M5.Lcd.getCursorX(), y, 2, COLOR_HUMIDITY, " H:" );
dispLcdColorBg( M5.Lcd.getCursorX(), y, 2, TFT_WHITE, bg_h_max, "%5.2lf", humidity_max );
dispLcdColor( M5.Lcd.getCursorX(), y, 2, COLOR_PRESSURE, " P:" );
dispLcdColorBg( M5.Lcd.getCursorX(), y, 2, TFT_WHITE, bg_p_max, "%7.2lf", pressure_max );
y += 17;
dispLcdColor( 0, y, 1, TFT_WHITE, "Min" );
dispLcdColor( x, y, 2, COLOR_TEMPERATURE, "T:" );
dispLcdColorBg( M5.Lcd.getCursorX(), y, 2, TFT_WHITE, bg_t_min, "%5.2lf", temperature_min );
dispLcdColor( M5.Lcd.getCursorX(), y, 2, COLOR_HUMIDITY, " H:" );
dispLcdColorBg( M5.Lcd.getCursorX(), y, 2, TFT_WHITE, bg_h_min, "%5.2lf", humidity_min );
dispLcdColor( M5.Lcd.getCursorX(), y, 2, COLOR_PRESSURE, " P:" );
dispLcdColorBg( M5.Lcd.getCursorX(), y, 2, TFT_WHITE, bg_p_min, "%7.2lf", pressure_min );
if ( 0 ) {
temperature_prev = temperature;
humidity_prev = humidity;
pressure_prev = pressure;
}
#endif
#endif
#if defined( USE_LCD )
//dispLcdColor( 0, 0, 1, WHITE, "T:%s", temp_c );
//dispLcdColor( 0, 16, 1, WHITE, "H:%s", hum_c );
//dispLcdColor( 64, 8, 1, WHITE, "P:%s", pres_c );
dispLcdColor( 0, 0, 1, WHITE, "%5.2lf %5.2lf %7.2lf", temperature, humidity, pressure );
#if defined( USE_ANALOG )
int n, i;
n = sizeof( analog_gpio ) / sizeof( int );
for ( i = 0 ; i < n ; i++ ) {
dispLcdColor( i * 30 , 10, 1, WHITE, "%4d ", analogRead( analog_gpio[i] ));
}
#endif
#endif
}
//
// 違
//
#define GRAPH_X 0
#define GRAPH_Y 39
#define GRAPH_WIDTH 320
#define GRAPH_HEIGHT 100
#define GRAPH_SEED 3
#define GRAPH_DATA_MAX (GRAPH_WIDTH-2)
#define GRAPH_INTERVAL 5
#define SEED_TEMPERATURE 0
#define SEED_HUMIDITY 1
#define SEED_PRESSURE 2
void initGraph()
{
#if defined( USE_M5STACK )
M5.Lcd.fillRect( GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT, BLACK );
M5.Lcd.drawRect( GRAPH_X, GRAPH_Y, GRAPH_WIDTH, GRAPH_HEIGHT, WHITE );
#endif
}
void clearGraph()
{
#if defined( USE_M5STACK )
M5.Lcd.fillRect( GRAPH_X + 1, GRAPH_Y + 1, GRAPH_WIDTH - 2, GRAPH_HEIGHT - 2, BLACK );
#endif
}
double graphData[GRAPH_DATA_MAX][GRAPH_SEED];
uint16_t graphDataN = 0;
/*
筝絎с若帥篆絖
篆絖翫 true, ∴鐚鐚經 false菴
*/
bool setGraphDataInterval( double temperature, double humidity, double pressure )
{
static time_t nextTime = 0L;
if ( nextTime > now())
return false;
nextTime = now() + GRAPH_INTERVAL; // 違祟羯
setGraphData( temperature, humidity, pressure );
return true;
}
void setGraphData( double temperature, double humidity, double pressure )
{
if ( graphDataN < GRAPH_DATA_MAX ) {
} else {
int i, j;
for ( i = 0 ; i < GRAPH_DATA_MAX - 1; i++ ) {
for ( j = 0; j < GRAPH_SEED; j++ ) {
graphData[i][j] = graphData[i + 1][j];
}
}
graphDataN--;
}
//Serial.println( graphDataN );
graphData[graphDataN][SEED_TEMPERATURE] = temperature;
graphData[graphDataN][SEED_HUMIDITY] = humidity;
graphData[graphDataN][SEED_PRESSURE] = pressure;
graphDataN++;
}
void drawGraphAll()
{
clearGraph();
drawGraph( SEED_TEMPERATURE, COLOR_TEMPERATURE );
drawGraph( SEED_HUMIDITY, COLOR_HUMIDITY );
drawGraph( SEED_PRESSURE, COLOR_PRESSURE );
}
void drawGraph(uint16_t seed, uint16_t color)
{
double max = -10000.0;
double min = 10000.0;
int x, y;
for ( x = 0; x < graphDataN; x++ ) {
if ( max < graphData[x][seed] )
max = graphData[x][seed];
if ( min > graphData[x][seed] )
min = graphData[x][seed];
}
if ( max == min ) {
max += 0.5;
min -= 0.5;
} else {
max += 0.1;
min -= 0.1;
}
for ( x = 0 ; x < graphDataN; x++ ) {
y = GRAPH_Y + GRAPH_HEIGHT - ((graphData[x][seed] - min) / (max - min ) * GRAPH_HEIGHT);
//Serial.print( y ); Serial.print( "," );
#if defined( USE_M5STACK )
M5.Lcd.drawPixel( x + 1, y, color );
#endif
}
//Serial.println( "" );
}
#endif
//
// Morse LED
//
#ifdef USE_MORSE_LED
void statusMorse()
{
double temperature, pressure, humidity;
char buf[256];
readThp(&temperature, &pressure, &humidity);
sprintf( buf, "T %.0f H %2d P %d", temperature, (int)humidity, (int)pressure );
setMorse( buf );
}
void doMorseLed()
{
if ( morse_led == 0 )
return;
execMorseFromQue();
}
//
// Todo: 篁ヤ磧祚礬禮
//
#define MORSE_QUE_MAX 10
int morse_que_n = 0;
char * morse_que[ MORSE_QUE_MAX ];
void setMorse( const char * str )
{
// Serial.printf( "setMorse(): %s Length=%d Que=%d\n", str, strlen( str ), morse_que_n );
char * p;
if ( morse_que_n >= MORSE_QUE_MAX ) {
Serial.println( "Error: too many morse_que in setMorse()" );
return;
}
if ( (p = (char *)malloc( strlen( str ) + 1 )) == NULL ) {
Serial.println( "Can't allocate memory in setMorse()" );
return;
}
// Serial.printf( "address is %ld\n", p );
memcpy( p, str, strlen( str ) + 1 );
morse_que[morse_que_n] = p;
morse_que_n++;
}
int getMorseQueN()
{
return morse_que_n;
}
void execMorseFromQue()
{
static boolean exec = false;
int q;
if ( exec != false )
return;
if ( morse_que_n == 0 )
return;
exec = true;
// Serial.printf( "execMorseFromQue(): start\n" );
Serial.printf( "MORSE: %s\n", morse_que[0] );
ledMorse( morse_que[0]);
free( morse_que[0] );
for ( q = 0 ; q < morse_que_n - 1 ; q++ ) {
morse_que[q] = morse_que[q + 1];
}
exec = false;
morse_que_n--;
// Serial.printf( "execMorseFromQue(): done\n" );
}
//
//
//
void ledMorse( char * str )
{
int n, i;
n = strlen( str );
for ( i = 0 ; i < n ; i++ ) {
ledMorseChar( str[i] );
}
}
const char * morse_code[] = {
"_______", "", "", "", "", "", "", "", "", "", "", "", "", "", "010101" /* or "010". */, "", /* !-/ 32 to 47 */
"11111", "01111", "00111", "00011", "00001", "00000", "10000", "11000", "11100", "11110", // 0-9
// "1", "01", "001", "0001", "00001", "00000", "10000", "1000", "100", "10", // 0-9 ュ
"", "", "", "", "", "", "", // :;<=>?@
"01", "1000", "1010", "100", "0", "0010", "110", "0000", "00", "0111", "101", "0100", "11", // A-M
"10", "111", "0110", "1101", "010", "000", "1", "001", "0001", "011", "1001", "1011", "1100", // N-Z
};
void ledMorseChar( char c )
{
int n, i;
int code = c - ' ';
char s;
/*
Serial.printf( "sizeof(morse_code ) is %d\n", sizeof( morse_code ) / sizeof( char * ));
return;
*/
if ( code < 0 || code > sizeof( morse_code ) / sizeof( char * )) {
Serial.printf( "Warning: Illegal Morse Char code, code is %d, %c\n", c, c );
return;
}
// Serial.println( code );
n = strlen( morse_code[code] );
// Serial.println( n );
//Serial.printf( "MORSE: %c %s\n", c, morse_code[code] );
for ( i = 0 ; i < n ; i++ ) {
switch ( morse_code[code][i] ) {
case '1':
// Serial.print( "L" );
digitalWrite( morse_led, HIGH );
delay( morse_speed * 3 );
digitalWrite( morse_led, LOW );
delay( morse_speed * 1 );
break;
case '0':
// Serial.print( "S" );
digitalWrite( morse_led, HIGH );
delay( morse_speed * 1 );
digitalWrite( morse_led, LOW );
delay( morse_speed * 1 );
break;
case '_':
delay( morse_speed * 1 );
break;
}
// delay( morse_speed * 3 );
}
//Serial.println();
delay( morse_speed * 3 );
}
#endif
#if defined( USE_OTA )
// ref. sample sketch BasicOTA.ino
void initOta()
{
ArduinoOTA
.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH)
type = "sketch";
else // U_SPIFFS
type = "filesystem";
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
Serial.println("Start updating " + type);
})
.onEnd([]() {
Serial.println("\nEnd");
})
.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
})
.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
else if (error == OTA_END_ERROR) Serial.println("End Failed");
});
ArduinoOTA.begin();
Serial.println( "OTA Enabled" );
}
#endif