add sort of working esp32 code

This commit is contained in:
Cyberes 2024-06-27 22:35:58 -06:00
parent 28cb7ecc43
commit 56a4e0fac9
22 changed files with 4313 additions and 0 deletions

4
esp32/README.md Normal file
View File

@ -0,0 +1,4 @@
```shell
git clone https://github.com/rweather/arduinolibs
cp -r arduinolibs/libraries/Crypto Freematics/firmware_v5/telelogger/lib
```

5
esp32/telelogger/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

View File

@ -0,0 +1,473 @@
menu "Telelogger Configuration"
config ENABLE_OBD
bool "Enable OBD Connection"
default "y"
config ENABLE_MEMS
bool "Enable MEMS Motion Sensor"
default "y"
choice STORAGE
bool "Storage Option"
default STORAGE_SD
config STORAGE_NONE
bool "No Storage"
config STORAGE_SPIFFS
bool "SPIFFS"
config STORAGE_SD
bool "MicroSD"
endchoice
config STORAGE
int
default 0 if STORAGE_NONE
default 1 if STORAGE_SPIFFS
default 2 if STORAGE_SD
choice GNSS
bool "GNSS Option"
default GNSS_INTERNAL
config GNSS_NONE
bool "No GNSS"
config GNSS_INTERNAL
bool "Internal GNSS"
config GNSS_EXTERNAL
bool "External GNSS"
config GNSS_CELLULAR
bool "Cellular GNSS"
endchoice
config GNSS
int
default 0 if GNSS_NONE
default 1 if GNSS_INTERNAL
default 2 if GNSS_EXTERNAL
default 3 if GNSS_CELLULAR
config BOARD_HAS_PSRAM
bool "Enable PSRAM"
default "n"
help
Enabling PSRAM and use it for data buffer
config ENABLE_BLE
bool "Enable BLE"
default "n"
help
Enabling BLE GATT server working with Freematics Controller app
config ENABLE_WIFI
bool "Enable Wi-Fi"
default "n"
help
Enabling seamless Wi-Fi and cellular network co-working
config WIFI_SSID
string "WiFi Hotspot SSID"
default ""
config WIFI_PASSWORD
string "WiFi Hotspot Password"
default ""
config ENABLE_HTTPD
bool "Enable Wi-Fi AP and HTTPd"
default "n"
help
Enabling Wi-Fi AP and HTTPd
config CELL_APN
string "Cellular Network APN"
default ""
help
If left blank, APN is obtained automatically if supported
choice SERVER_PROTOCOL
bool "Server Transport Protocol"
default PROTOCOL_UDP
config PROTOCOL_UDP
bool "UDP Protocol"
config PROTOCOL_HTTP
bool "HTTP Protocol"
config PROTOCOL_HTTPS
bool "HTTPS Protocol"
endchoice
config SERVER_PROTOCOL
int
default 1 if PROTOCOL_UDP
default 2 if PROTOCOL_HTTP
default 3 if PROTOCOL_HTTPS
config SERVER_HOST
string "Server Host/IP"
default "hub.freematics.com"
config SERVER_PORT
int "Server Port (0 for auto)"
range 0 65535
default 0
endmenu
menu "Arduino Configuration"
config ENABLE_ARDUINO_DEPENDS
bool
select LWIP_SO_RCVBUF
select ETHERNET
select WIFI_ENABLED
select ESP32_PHY_CALIBRATION_AND_DATA_STORAGE if IDF_TARGET_ESP32
select MEMMAP_SMP
default "y"
config AUTOSTART_ARDUINO
bool "Autostart Arduino setup and loop on boot"
default "y"
help
Enabling this option will implement app_main and start Arduino.
All you need to implement in your main.cpp is setup() and loop()
and include Arduino.h
If disabled, you can call initArduino() to run any preparations
required by the framework
choice ARDUINO_RUNNING_CORE
bool "Core on which Arduino's setup() and loop() are running"
default ARDUINO_RUN_CORE1
help
Select on which core Arduino's setup() and loop() functions run
config ARDUINO_RUN_CORE0
bool "CORE 0"
config ARDUINO_RUN_CORE1
bool "CORE 1"
config ARDUINO_RUN_NO_AFFINITY
bool "BOTH"
endchoice
config ARDUINO_RUNNING_CORE
int
default 0 if ARDUINO_RUN_CORE0
default 1 if ARDUINO_RUN_CORE1
default -1 if ARDUINO_RUN_NO_AFFINITY
config ARDUINO_LOOP_STACK_SIZE
int "Loop thread stack size"
default 8192
help
Amount of stack available for the Arduino task.
choice ARDUINO_EVENT_RUNNING_CORE
bool "Core on which Arduino's event handler is running"
default ARDUINO_EVENT_RUN_CORE1
help
Select on which core Arduino's WiFi.onEvent() run
config ARDUINO_EVENT_RUN_CORE0
bool "CORE 0"
config ARDUINO_EVENT_RUN_CORE1
bool "CORE 1"
config ARDUINO_EVENT_RUN_NO_AFFINITY
bool "BOTH"
endchoice
config ARDUINO_EVENT_RUNNING_CORE
int
default 0 if ARDUINO_EVENT_RUN_CORE0
default 1 if ARDUINO_EVENT_RUN_CORE1
default -1 if ARDUINO_EVENT_RUN_NO_AFFINITY
choice ARDUINO_UDP_RUNNING_CORE
bool "Core on which Arduino's UDP is running"
default ARDUINO_UDP_RUN_CORE1
help
Select on which core Arduino's UDP run
config ARDUINO_UDP_RUN_CORE0
bool "CORE 0"
config ARDUINO_UDP_RUN_CORE1
bool "CORE 1"
config ARDUINO_UDP_RUN_NO_AFFINITY
bool "BOTH"
endchoice
config ARDUINO_UDP_TASK_PRIORITY
int "Priority of the UDP task"
default 3
help
Select at what priority you want the UDP task to run.
config ARDUINO_UDP_RUNNING_CORE
int
default 0 if ARDUINO_UDP_RUN_CORE0
default 1 if ARDUINO_UDP_RUN_CORE1
default -1 if ARDUINO_UDP_RUN_NO_AFFINITY
config ARDUINO_ISR_IRAM
bool "Run interrupts in IRAM"
default "n"
help
Enabling this option will Attach all interrupts with the IRAm flag.
It will also make some HAL function, like, digitalRead/Write and more
be loaded into IRAM for access inside ISRs.
Beware that this is a very dangerous setting. Enable it only if you
are fully aware of the consequences.
config DISABLE_HAL_LOCKS
bool "Disable mutex locks for HAL"
default "n"
help
Enabling this option will run all hardware abstraction without locks.
While communication with external hardware will be faster, you need to
make sure that there is no option to use the same bus from another thread
or interrupt at the same time. Option is best used with Arduino enabled
and code implemented only in setup/loop and Arduino callbacks
menu "Debug Log Configuration"
choice ARDUHAL_LOG_DEFAULT_LEVEL
bool "Default log level"
default ARDUHAL_LOG_DEFAULT_LEVEL_ERROR
help
Specify how much output to see in logs by default.
config ARDUHAL_LOG_DEFAULT_LEVEL_NONE
bool "No output"
config ARDUHAL_LOG_DEFAULT_LEVEL_ERROR
bool "Error"
config ARDUHAL_LOG_DEFAULT_LEVEL_WARN
bool "Warning"
config ARDUHAL_LOG_DEFAULT_LEVEL_INFO
bool "Info"
config ARDUHAL_LOG_DEFAULT_LEVEL_DEBUG
bool "Debug"
config ARDUHAL_LOG_DEFAULT_LEVEL_VERBOSE
bool "Verbose"
endchoice
config ARDUHAL_LOG_DEFAULT_LEVEL
int
default 0 if ARDUHAL_LOG_DEFAULT_LEVEL_NONE
default 1 if ARDUHAL_LOG_DEFAULT_LEVEL_ERROR
default 2 if ARDUHAL_LOG_DEFAULT_LEVEL_WARN
default 3 if ARDUHAL_LOG_DEFAULT_LEVEL_INFO
default 4 if ARDUHAL_LOG_DEFAULT_LEVEL_DEBUG
default 5 if ARDUHAL_LOG_DEFAULT_LEVEL_VERBOSE
config ARDUHAL_LOG_COLORS
bool "Use ANSI terminal colors in log output"
default "n"
help
Enable ANSI terminal color codes in bootloader output.
In order to view these, your terminal program must support ANSI color codes.
config ARDUHAL_ESP_LOG
bool "Forward ESP_LOGx to Arduino log output"
default "n"
help
This option will redefine the ESP_LOGx macros to Arduino's log_x macros.
To enable for your application, add the follwing after your includes:
#ifdef ARDUINO_ARCH_ESP32
#include "esp32-hal-log.h"
#endif
endmenu
choice ARDUHAL_PARTITION_SCHEME
bool "Used partition scheme"
default ARDUHAL_PARTITION_SCHEME_DEFAULT
help
Specify which partition scheme to be used.
config ARDUHAL_PARTITION_SCHEME_DEFAULT
bool "Default"
config ARDUHAL_PARTITION_SCHEME_MINIMAL
bool "Minimal (for 2MB FLASH)"
config ARDUHAL_PARTITION_SCHEME_NO_OTA
bool "No OTA (for large apps)"
config ARDUHAL_PARTITION_SCHEME_HUGE_APP
bool "Huge App (for very large apps)"
config ARDUHAL_PARTITION_SCHEME_MIN_SPIFFS
bool "Minimal SPIFFS (for large apps with OTA)"
endchoice
config ARDUHAL_PARTITION_SCHEME
string
default "default" if ARDUHAL_PARTITION_SCHEME_DEFAULT
default "minimal" if ARDUHAL_PARTITION_SCHEME_MINIMAL
default "no_ota" if ARDUHAL_PARTITION_SCHEME_NO_OTA
default "huge_app" if ARDUHAL_PARTITION_SCHEME_HUGE_APP
default "min_spiffs" if ARDUHAL_PARTITION_SCHEME_MIN_SPIFFS
config AUTOCONNECT_WIFI
bool "Autoconnect WiFi on boot"
default "n"
depends on AUTOSTART_ARDUINO
select ARDUINO_SELECTIVE_WiFi
help
If enabled, WiFi will connect to the last used SSID (if station was enabled),
else connection will be started only after calling WiFi.begin(ssid, password)
config ARDUINO_SELECTIVE_COMPILATION
bool "Include only specific Arduino libraries"
default n
config ARDUINO_SELECTIVE_ArduinoOTA
bool "Enable ArduinoOTA"
depends on ARDUINO_SELECTIVE_COMPILATION
select ARDUINO_SELECTIVE_WiFi
select ARDUINO_SELECTIVE_ESPmDNS
default y
config ARDUINO_SELECTIVE_AsyncUDP
bool "Enable AsyncUDP"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
config ARDUINO_SELECTIVE_AzureIoT
bool "Enable AzureIoT"
depends on ARDUINO_SELECTIVE_COMPILATION
select ARDUINO_SELECTIVE_HTTPClient
default y
config ARDUINO_SELECTIVE_BLE
bool "Enable BLE"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
config ARDUINO_SELECTIVE_BluetoothSerial
bool "Enable BluetoothSerial"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
config ARDUINO_SELECTIVE_DNSServer
bool "Enable DNSServer"
depends on ARDUINO_SELECTIVE_COMPILATION
select ARDUINO_SELECTIVE_WiFi
default y
config ARDUINO_SELECTIVE_EEPROM
bool "Enable EEPROM"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
config ARDUINO_SELECTIVE_ESP32
bool "Enable ESP32"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
config ARDUINO_SELECTIVE_ESPmDNS
bool "Enable ESPmDNS"
depends on ARDUINO_SELECTIVE_COMPILATION
select ARDUINO_SELECTIVE_WiFi
default y
config ARDUINO_SELECTIVE_FFat
bool "Enable FFat"
depends on ARDUINO_SELECTIVE_COMPILATION
select ARDUINO_SELECTIVE_FS
default y
config ARDUINO_SELECTIVE_FS
bool "Enable FS"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
config ARDUINO_SELECTIVE_HTTPClient
bool "Enable HTTPClient"
depends on ARDUINO_SELECTIVE_COMPILATION
select ARDUINO_SELECTIVE_WiFi
select ARDUINO_SELECTIVE_WiFiClientSecure
default y
config ARDUINO_SELECTIVE_LITTLEFS
bool "Enable LITTLEFS"
depends on ARDUINO_SELECTIVE_COMPILATION
select ARDUINO_SELECTIVE_FS
default y
config ARDUINO_SELECTIVE_NetBIOS
bool "Enable NetBIOS"
depends on ARDUINO_SELECTIVE_COMPILATION
select ARDUINO_SELECTIVE_WiFi
default y
config ARDUINO_SELECTIVE_Preferences
bool "Enable Preferences"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
config ARDUINO_SELECTIVE_SD
bool "Enable SD"
depends on ARDUINO_SELECTIVE_COMPILATION
select ARDUINO_SELECTIVE_FS
default y
config ARDUINO_SELECTIVE_SD_MMC
bool "Enable SD_MMC"
depends on ARDUINO_SELECTIVE_COMPILATION
select ARDUINO_SELECTIVE_FS
default y
config ARDUINO_SELECTIVE_SimpleBLE
bool "Enable SimpleBLE"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
config ARDUINO_SELECTIVE_SPI
bool "Enable SPI"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
config ARDUINO_SELECTIVE_SPIFFS
bool "Enable SPIFFS"
depends on ARDUINO_SELECTIVE_COMPILATION
select ARDUINO_SELECTIVE_FS
default y
config ARDUINO_SELECTIVE_Ticker
bool "Enable Ticker"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
config ARDUINO_SELECTIVE_Update
bool "Enable Update"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
config ARDUINO_SELECTIVE_WebServer
bool "Enable WebServer"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
select ARDUINO_SELECTIVE_FS
config ARDUINO_SELECTIVE_WiFi
bool "Enable WiFi"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
config ARDUINO_SELECTIVE_WiFiClientSecure
bool "Enable WiFiClientSecure"
depends on ARDUINO_SELECTIVE_COMPILATION
select ARDUINO_SELECTIVE_WiFi
default y
config ARDUINO_SELECTIVE_WiFiProv
bool "Enable WiFiProv"
depends on ARDUINO_SELECTIVE_COMPILATION
select ARDUINO_SELECTIVE_WiFi
default y
config ARDUINO_SELECTIVE_Wire
bool "Enable Wire"
depends on ARDUINO_SELECTIVE_COMPILATION
default y
endmenu

View File

@ -0,0 +1,49 @@
This Arduino sketch is developed for [Freematics ONE+](https://freematics.com/products/freematics-one-plus/) to collect vehicle telemetry data from OBD, GPS, motion sensor, to log the data in local storage and to transmit the data to a remote server in real-time. It demonstrates most capabilities of Freematics ONE+ and works well with [Traccar](https://www.traccar.org) GPS tracking platform.
Data Collection
---------------
The sketch collects following data.
* Vehicle OBD data (from OBD port)
* Battery voltage (from OBD port)
* Geolocation data (from internal or external GNSS)
* Accelerometer and gyroscope data (from internal MEMS motion sensor)
* Cellular or WiFi network signal level
* Device temperature
Collected data are stored in a circular buffer in ESP32's IRAM or PSRAM. When PSRAM is enabled, hours of data can be buffered in case of temporary network outage and transmitted when network connection resumes.
Data Transmission
-----------------
Data transmission over UDP and HTTP(s) protocols are implemented for the followings.
* WiFi (ESP32 built-in)
* 3G WCDMA (SIM5360)
* 4G LTE CAT-4 (SIM7600)
* 4G LTE CAT-M (SIM7070)
UDP mode implements a telemetry client for [Freematics Hub](https://hub.freematics.com) and [Traccar](https://www.traccar.org). HTTP(s) mode implements [OsmAnd](https://www.traccar.org/osmand/) protocol with additional data sent as POST payload.
Seamless WiFi and cellular network co-working is implemented. When defined WiFi hotspot is available, data is transmitted via WiFi and cellular module is switched off. When no WiFi hotspot can be reached, cellular module is switched on for data transmission until WiFi hotspot available again.
Data Storage
------------
Following types of data storage are supported.
* MicroSD card storage
* ESP32 built-in Flash memory storage (SPIFFS)
BLE & App
---------
A BLE SPP server is implemented in [FreematicsPlus](https://github.com/stanleyhuangyc/Freematics/blob/master/libraries/FreematicsPlus) library. To enable BLE support, change ENABLE_BLE to 1 [config.h](config.h). This will enable remote control and data monitoring via [Freematics Controller App](https://freematics.com/software/freematics-controller/).
Prerequisites
-------------
* Freematics ONE+ [Model A](https://freematics.com/products/freematics-one-plus/), [Model B](https://freematics.com/products/freematics-one-plus-model-b/), [Model H](https://freematics.com/products/freematics-one-plus-model-h/)
* A micro SIM card if cellular network connectivity required
* [PlatformIO](http://platformio.org/), [Arduino IDE](https://github.com/espressif/arduino-esp32#installation-instructions), [Freematics Builder](https://freematics.com/software/arduino-builder) or [ESP-IDF](https://github.com/espressif/esp-idf) for compiling and uploading code

201
esp32/telelogger/config.h Normal file
View File

@ -0,0 +1,201 @@
#ifndef CONFIG_H_INCLUDED
#define CONFIG_H_INCLUDED
#ifdef CONFIG_ENABLE_OBD
#define ENABLE_OBD CONFIG_ENABLE_OBD
#endif
#ifdef CONFIG_ENABLE_MEMS
#define ENABLE_MEMS CONFIG_ENABLE_MEMS
#endif
#ifdef CONFIG_GNSS
#define GNSS CONFIG_GNSS
#endif
#ifdef CONFIG_STORAGE
#define STORAGE CONFIG_STORAGE
#endif
#ifdef CONFIG_BOARD_HAS_PSRAM
#define BOARD_HAS_PSRAM 1
#endif
#ifdef CONFIG_ENABLE_WIFI
#define ENABLE_WIFI CONFIG_ENABLE_WIFI
#define WIFI_SSID CONFIG_WIFI_SSID
#define WIFI_PASSWORD CONFIG_WIFI_PASSWORD
#endif
#ifdef CONFIG_ENABLE_BLE
#define ENABLE_BLE CONFIG_ENABLE_BLE
#endif
#ifdef CONFIG_ENABLE_HTTPD
#define ENABLE_HTTPD CONFIG_ENABLE_HTTPD
#endif
#ifdef CONFIG_SERVER_HOST
#define SERVER_HOST CONFIG_SERVER_HOST
#define SERVER_PORT CONFIG_SERVER_PORT
#define SERVER_PROTOCOL CONFIG_SERVER_PROTOCOL
#endif
#ifdef CONFIG_CELL_APN
#define CELL_APN CONFIG_CELL_APN
#endif
/**************************************
* Circular Buffer Configuration
**************************************/
#if BOARD_HAS_PSRAM
#define BUFFER_SLOTS 1024 /* max number of buffer slots */
#define BUFFER_LENGTH 384 /* bytes per slot */
#define SERIALIZE_BUFFER_SIZE 4096 /* bytes */
#else
#define BUFFER_SLOTS 32 /* max number of buffer slots */
#define BUFFER_LENGTH 256 /* bytes per slot */
#define SERIALIZE_BUFFER_SIZE 1024 /* bytes */
#endif
/**************************************
* Configuration Definitions
**************************************/
#define STORAGE_NONE 0
#define STORAGE_SPIFFS 1
#define STORAGE_SD 2
#define GNSS_NONE 0
#define GNSS_STANDALONE 1
#define GNSS_CELLULAR 2
#define PROTOCOL_UDP 1
#define PROTOCOL_HTTP 2
#define PROTOCOL_HTTPS 3
#define PROTOCOL_METHOD_GET 0
#define PROTOCOL_METHOD_POST 1
/**************************************
* OBD-II configurations
**************************************/
#ifndef ENABLE_OBD
#define ENABLE_OBD 1
#endif
// maximum consecutive OBD access errors before entering standby
#define MAX_OBD_ERRORS 3
/**************************************
* Networking configurations
**************************************/
#ifndef ENABLE_WIFI
#define ENABLE_WIFI 1
// WiFi settings
#define WIFI_SSID "example"
#define WIFI_PASSWORD "1234"
#endif
#ifndef SERVER_HOST
// cellular network settings
#define CELL_APN "hologram"
// Freematics Hub server settings
#define SERVER_HOST "192.168.1.114"
#define SERVER_PROTOCOL PROTOCOL_UDP
#endif
#define SERVER_ENCRYPTION_ENABLE 1
#define CHACHA20_KEY "d38a3b96a26d0b1139bd30c174884f5dbc8eaaf492493725633ecebfa4ab19e9"
#ifndef CONFIG_MBEDTLS_CHACHAPOLY_C
#define CONFIG_MBEDTLS_CHACHAPOLY_C y
#endif
// SIM card setting
#define SIM_CARD_PIN ""
// HTTPS settings
#define SERVER_METHOD PROTOCOL_METHOD_POST
#define SERVER_PATH "/hub/api"
#if !SERVER_PORT
#undef SERVER_PORT
#if SERVER_PROTOCOL == PROTOCOL_UDP
#define SERVER_PORT 5171
#elif SERVER_PROTOCOL == PROTOCOL_HTTP
#define SERVER_PORT 80
#elif SERVER_PROTOCOL == PROTOCOL_HTTPS
#define SERVER_PORT 443
#endif
#endif
// WiFi Mesh settings
#define WIFI_MESH_ID "123456"
#define WIFI_MESH_CHANNEL 13
// WiFi AP settings
#define WIFI_AP_SSID "TELELOGGER"
#define WIFI_AP_PASSWORD "PASSWORD"
// maximum consecutive communication errors before resetting network
#define MAX_CONN_ERRORS_RECONNECT 5
// maximum allowed connecting time
#define MAX_CONN_TIME 10000 /* ms */
// data receiving timeout
#define DATA_RECEIVING_TIMEOUT 5000 /* ms */
// expected maximum server sync signal interval
#define SERVER_SYNC_INTERVAL 120 /* seconds, 0 to disable */
// data interval settings
#define STATIONARY_TIME_TABLE {10, 60, 180} /* seconds */
#define DATA_INTERVAL_TABLE {1000, 2000, 5000} /* ms */
#define PING_BACK_INTERVAL 900 /* seconds */
#define SIGNAL_CHECK_INTERVAL 10 /* seconds */
/**************************************
* Data storage configurations
**************************************/
#ifndef STORAGE
// change the following line to change storage type
#define STORAGE STORAGE_NONE
#endif
/**************************************
* MEMS sensors
**************************************/
#ifndef ENABLE_MEMS
#define ENABLE_MEMS 1
#endif
/**************************************
* GPS
**************************************/
#ifndef GNSS
// change the following line to change GNSS setting
#define GNSS GNSS_STANDALONE
#endif
// keeping GNSS power on during standby
#define GNSS_ALWAYS_ON 0
// GNSS reset timeout while no signal
#define GNSS_RESET_TIMEOUT 300 /* seconds */
/**************************************
* Standby/wakeup
**************************************/
// motion threshold for waking up
#define MOTION_THRESHOLD 0.4f /* vehicle motion threshold in G */
// engine jumpstart voltage for waking up (when MEMS unavailable)
#define JUMPSTART_VOLTAGE 14 /* V */
// reset device after waking up
#define RESET_AFTER_WAKEUP 1
/**************************************
* Additional features
**************************************/
#define PIN_SENSOR1 34
#define PIN_SENSOR2 26
#define COOLING_DOWN_TEMP 75 /* celsius degrees */
// enable(1)/disable(0) http server
#ifndef ENABLE_HTTPD
#define ENABLE_HTTPD 0
#endif
// enable(1)/disable(0) BLE SPP server (for Freematics Controller App).
#ifndef ENABLE_BLE
#define ENABLE_BLE 0
#endif
#endif // CONFIG_H_INCLUDED

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<config>
<target name="Freematics ONE+" board="esp32 esp32c3" monitor_baudrate="115200">
<define name="Enable OBD" type="bool" const="ENABLE_OBD" default="true"/>
<define name="Enable Motion Sensor" type="bool" const="ENABLE_MEMS" default="true"/>
<define name="GNSS">
<option name="None" const="GNSS=GNSS_NONE"/>
<option name="Standalone GNSS" const="GNSS=GNSS_STANDALONE" default="true"/>
<option name="Cellular GNSS" const="GNSS=GNSS_CELLULAR"/>
</define>
<define name="Storage">
<option name="None" const="STORAGE=STORAGE_NONE"/>
<option name="SPIFFS" const="STORAGE=STORAGE_SPIFFS"/>
<option name="MicroSD" const="STORAGE=STORAGE_SD" default="true"/>
</define>
<define name="External Sensors">
<option name="None" const="LOG_EXT_SENSORS=0"/>
<option name="Digital Input" const="LOG_EXT_SENSORS=1"/>
<option name="Analog Input" const="LOG_EXT_SENSORS=2"/>
</define>
<define name="Cellular Network APN" const="CELL_APN" type="string" default=""/>
<define name="WiFi Network" const="ENABLE_WIFI" default="true" type="bool"/>
<define name="WiFi SSID" const="WIFI_SSID" type="string" dep="ENABLE_WIFI"/>
<define name="WiFi Password" const="WIFI_PASSWORD" type="string" dep="ENABLE_WIFI"/>
<define name="Server Host" const="SERVER_HOST" type="string" default="hub.freematics.com"/>
<define name="Server Port (0 for auto)" const="SERVER_PORT" type="number" default="0"/>
<define name="Transport Protocol">
<option name="UDP" const="SERVER_PROTOCOL=PROTOCOL_UDP"/>
<option name="HTTP" const="SERVER_PROTOCOL=PROTOCOL_HTTP"/>
<option name="HTTPS" const="SERVER_PROTOCOL=PROTOCOL_HTTPS"/>
</define>
<define name="Enable PSRAM" type="bool" const="BOARD_HAS_PSRAM"/>
</target>
</config>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,126 @@
var source;
var origin;
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event) {
//event.source.postMessage(event.data,event.origin);
source = event.source;
origin = event.origin;
processInput(event.data);
}
function sendMessage(data) {
if (source) source.postMessage(data, origin);
}
function checkData(data, key)
{
var i;
if ((i = data.lastIndexOf(key)) >= 0) {
var j = data.indexOf("\r", i);
return j >= 0 ? data.substr(i + key.length, j - i - key.length) : data.substr(i + key.length);
}
return null;
}
var imgTick = "<img src='tick.png'/>";
var imgCross = "<img src='cross.png'/>";
var con = "";
var inited = false;
function processInput(data)
{
var i;
var ret;
if (con.length > 1024) con = con.substr(512);
con += data;
if (!inited) {
if (ret = checkData(con, "FLASH:")) {
document.getElementById("flash_size").innerText = ret;
}
if (ret = checkData(con, "PSRAM:")) {
document.getElementById("psram_size").innerText = ret.substr(0, 2) == "E " ? "N/A" : ret;
}
if (ret = checkData(con, "TYPE:")) {
var type = parseInt(ret);
var typeName = "Unknown";
if (type == 11 || type == 16) {
typeName = "Freematics ONE+ Model A";
} else if (type >= 12 && type <= 14) {
typeName = "Freematics ONE+ Model B";
} else if (type == 15) {
typeName = "Freematics ONE+ Model H";
}
document.getElementById("devtype").innerText = typeName;
}
if (ret = checkData(con, "DEVICE ID:")) {
document.getElementById("devid").value = ret;
}
if (ret = checkData(con, "RTC:")) {
document.getElementById("rtc").innerText = ret;
}
if (ret = checkData(con, "SD:")) {
document.getElementById("sd_size").innerHTML = ret;
}
if (ret = checkData(con, "NO SD CARD") != null) {
document.getElementById("sd_size").innerHTML = "NO CARD";
}
if (ret = checkData(con, "GNSS:")) {
document.getElementById("gps").innerHTML = ret.indexOf("NO") >= 0 ? imgCross : imgTick;
}
if (ret = checkData(con, "OBD:")) {
document.getElementById("obd").innerHTML = ret.indexOf("NO") >= 0 ? imgCross : imgTick;
}
if (ret = checkData(con, "MEMS:")) {
document.getElementById("mems").innerHTML = ret.indexOf("NO") >= 0 ? imgCross : (imgTick + " " + ret);
}
if (ret = checkData(con, "HTTPD:")) {
document.getElementById("wifi").innerHTML = ret.indexOf("NO") >= 0 ? imgCross : imgTick;
}
if (ret = checkData(con, "WiFi IP:")) {
document.getElementById("wifi").innerHTML = imgTick + " IP:" + ret;
}
if (ret = checkData(con, "IMEI:")) {
document.getElementById("imei").innerText = "IMEI:" + ret;
}
if (ret = checkData(con, "CELL:")) {
document.getElementById("cellinfo").innerHTML = ret == "NO" ? imgCross : (imgTick + " " + ret);
}
if ((ret = checkData(con, "NO SIM CARD")) != null) {
document.getElementById("imei").innerHTML = "| NO SIM CARD";
}
if (ret = checkData(con, "Operator:")) {
document.getElementById("imei").innerText = "| " + ret;
}
if (ret = checkData(con, "Unable to connect") != null) {
document.getElementById("server").innerHTML = imgCross;
}
if (ret = checkData(con, "LOGIN")) {
document.getElementById("server").innerText = "Connecting to server" + ret;
}
}
if (ret = checkData(con, "[NET]")) {
document.getElementById("server").innerText = ret;
inited = true;
}
if (ret = checkData(con, "[BUF]")) {
document.getElementById("buffer").innerText = ret;
}
if (ret = checkData(con, "[FILE]")) {
document.getElementById("file").innerText = ret;
}
if (ret = checkData(con, "[GPS]")) {
document.getElementById("gps").innerText = ret;
}
if (ret = checkData(con, "[WIFI]")) {
document.getElementById("wifi").innerText = ret;
}
if (ret = checkData(con, "[CELL]")) {
document.getElementById("cell").innerText = ret;
}
if (ret = checkData(con, "RSSI:")) {
document.getElementById("rssi").innerText = "| RSSI:" + ret;
}
}

View File

@ -0,0 +1,33 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
body{ margin:0; padding:0; border:none;font-size:16px; color:#3f3c8e; background:#dce4ed; font-family: Verdana, Geneva, Tahoma, sans-serif}
a:link, a:visited{ color:#fff; text-decoration:underline;}
a:hover, a:active{ color:#FFC; text-decoration:underline;}
input { font-size: 16px; text-align:center; }
span { font-weight:bold; }
li { line-height: 28px; }
</style>
</head>
<body>
<ul>
<li>Device ID: <input type="text" id="devid" readonly /></li>
<li>Device Type: <span id="devtype"></span></li>
<li>FLASH: <span id="flash_size"></span></li>
<li>PSRAM: <span id="psram_size"></span></li>
<li>SD Card: <span id="sd_size"></span></li>
<li>Motion Sensor: <span id="mems"></span></li>
<li>GNSS: <span id="gps"></span></li>
<li>OBD: <span id="obd"></span></li>
<li>Cellular Module: <span id="cellinfo"></span> <span id="imei"></span></span></li>
<li>Cellular: <span id="cell"></span> <span id="rssi"></span></span></li>
<li>Wi-Fi: <span id="wifi"></span></li>
<li>Buffer: <span id="buffer"></span></li>
<li>Server: <span id="server"></span></li>
<li>File: <span id="file"></span></li>
</ul>
<script language="javascript" src="dashboard.js"></script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,353 @@
/*************************************************************************
* Vehicle Telemetry Data Logger for Freematics ONE+
*
* Developed by Stanley Huang <stanley@freematics.com.au>
* Distributed under BSD license
* Visit https://freematics.com/products/freematics-one-plus for more info
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* Implemented HTTP APIs:
* /api/info - device info
* /api/live - live data (OBD/GPS/MEMS)
* /api/control - issue a control command
* /api/list - list of log files
* /api/log/<file #> - raw CSV format log file
* /api/delete/<file #> - delete file
* /api/data/<file #>?pid=<PID in hex> - JSON array of PID data
*************************************************************************/
#include <SPI.h>
#include <FS.h>
#include <SD.h>
#include <SPIFFS.h>
#include <FreematicsPlus.h>
#include <WiFi.h>
#include <SPIFFS.h>
#include <apps/sntp/sntp.h>
#include <esp_spi_flash.h>
#include <esp_err.h>
#include <httpd.h>
#include "config.h"
#if ENABLE_HTTPD
#define WIFI_TIMEOUT 5000
extern uint32_t fileid;
extern "C"
{
uint8_t temprature_sens_read();
uint32_t hall_sens_read();
}
HttpParam httpParam;
int handlerLiveData(UrlHandlerParam* param);
int handlerControl(UrlHandlerParam* param);
uint16_t hex2uint16(const char *p);
int handlerInfo(UrlHandlerParam* param)
{
char *buf = param->pucBuffer;
int bufsize = param->bufSize;
int bytes = snprintf(buf, bufsize, "{\"httpd\":{\"uptime\":%u,\"clients\":%u,\"requests\":%u,\"traffic\":%u},\n",
(unsigned int)millis(), httpParam.stats.clientCount, (unsigned int)httpParam.stats.reqCount, (unsigned int)(httpParam.stats.totalSentBytes >> 10));
time_t now;
time(&now);
struct tm timeinfo = { 0 };
localtime_r(&now, &timeinfo);
if (timeinfo.tm_year) {
bytes += snprintf(buf + bytes, bufsize - bytes, "\"rtc\":{\"date\":\"%04u-%02u-%02u\",\"time\":\"%02u:%02u:%02u\"},\n",
timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
}
int deviceTemp = (int)temprature_sens_read() * 165 / 255 - 40;
bytes += snprintf(buf + bytes, bufsize - bytes, "\"cpu\":{\"temperature\":%d,\"magnetic\":%d},\n",
deviceTemp, hall_sens_read());
#if STORAGE == STORAGE_SPIFFS
bytes += snprintf(buf + bytes, bufsize - bytes, "\"spiffs\":{\"total\":%u,\"used\":%u}",
SPIFFS.totalBytes(), SPIFFS.usedBytes());
#else
bytes += snprintf(buf + bytes, bufsize - bytes, "\"sd\":{\"total\":%llu,\"used\":%llu}",
SD.totalBytes(), SD.usedBytes());
#endif
if (bytes < bufsize - 1) buf[bytes++] = '}';
param->contentLength = bytes;
param->contentType=HTTPFILETYPE_JSON;
return FLAG_DATA_RAW;
}
class LogDataContext {
public:
File file;
uint32_t tsStart;
uint32_t tsEnd;
uint16_t pid;
};
int handlerLogFile(UrlHandlerParam* param)
{
LogDataContext* ctx = (LogDataContext*)param->hs->ptr;
param->contentType = HTTPFILETYPE_TEXT;
if (ctx) {
if (!param->pucBuffer) {
// connection to be closed, final calling, cleanup
ctx->file.close();
delete ctx;
param->hs->ptr = 0;
return 0;
}
} else {
int id = 0;
if (param->pucRequest[0] == '/') {
id = atoi(param->pucRequest + 1);
}
sprintf(param->pucBuffer, "/DATA/%u.CSV", id == 0 ? fileid : id);
ctx = new LogDataContext;
#if STORAGE == STORAGE_SPIFFS
ctx->file = SPIFFS.open(param->pucBuffer, FILE_READ);
#else
ctx->file = SD.open(param->pucBuffer, FILE_READ);
#endif
if (!ctx->file) {
strcat(param->pucBuffer, " not found");
param->contentLength = strlen(param->pucBuffer);
delete ctx;
return FLAG_DATA_RAW;
}
param->hs->ptr = (void*)ctx;
}
if (!ctx->file.available()) {
// EOF
return 0;
}
param->contentLength = ctx->file.readBytes(param->pucBuffer, param->bufSize);
param->contentType = HTTPFILETYPE_TEXT;
return FLAG_DATA_STREAM;
}
int handlerLogData(UrlHandlerParam* param)
{
uint32_t duration = 0;
LogDataContext* ctx = (LogDataContext*)param->hs->ptr;
param->contentType = HTTPFILETYPE_JSON;
if (ctx) {
if (!param->pucBuffer) {
// connection to be closed, final calling, cleanup
ctx->file.close();
delete ctx;
param->hs->ptr = 0;
return 0;
}
} else {
int id = 0;
if (param->pucRequest[0] == '/') {
id = atoi(param->pucRequest + 1);
}
sprintf(param->pucBuffer, "/DATA/%u.CSV", id == 0 ? fileid : id);
ctx = new LogDataContext;
#if STORAGE == STORAGE_SPIFFS
ctx->file = SPIFFS.open(param->pucBuffer, FILE_READ);
#else
ctx->file = SD.open(param->pucBuffer, FILE_READ);
#endif
if (!ctx->file) {
param->contentLength = sprintf(param->pucBuffer, "{\"error\":\"Data file not found\"}");
delete ctx;
return FLAG_DATA_RAW;
}
ctx->pid = mwGetVarValueHex(param->pxVars, "pid", 0);
ctx->tsStart = mwGetVarValueInt(param->pxVars, "start", 0);
ctx->tsEnd = 0xffffffff;
duration = mwGetVarValueInt(param->pxVars, "duration", 0);
if (ctx->tsStart && duration) {
ctx->tsEnd = ctx->tsStart + duration;
duration = 0;
}
param->hs->ptr = (void*)ctx;
// JSON head
param->contentLength = sprintf(param->pucBuffer, "[");
}
int len = 0;
char buf[64];
uint32_t ts = 0;
for (;;) {
int c = ctx->file.read();
if (c == -1) {
if (param->contentLength == 0) {
// EOF
return 0;
}
// JSON tail
if (param->pucBuffer[param->contentLength - 1] == ',') param->contentLength--;
param->pucBuffer[param->contentLength++] = ']';
break;
}
if (c == '\n') {
// line end, process the line
buf[len] = 0;
char *value = strchr(buf, ',');
if (value++) {
uint16_t pid = hex2uint16(buf);
if (pid == 0) {
// timestamp
ts = atoi(value);
if (duration) {
ctx->tsEnd = ts + duration;
duration = 0;
}
} else if (pid == ctx->pid && ts >= ctx->tsStart && ts < ctx->tsEnd) {
// generate json array element
param->contentLength += snprintf(param->pucBuffer + param->contentLength, param->bufSize - param->contentLength,
"[%u,%s],", ts, value);
}
}
len = 0;
if (param->contentLength + 32 > param->bufSize) break;
} else if (len < sizeof(buf) - 1) {
buf[len++] = c;
}
}
return FLAG_DATA_STREAM;
}
int handlerLogList(UrlHandlerParam* param)
{
char *buf = param->pucBuffer;
int bufsize = param->bufSize;
File file;
#if STORAGE == STORAGE_SPIFFS
File root = SPIFFS.open("/");
#elif STORAGE == STORAGE_SD
File root = SD.open("/DATA");
#endif
int n = snprintf(buf, bufsize, "[");
if (root) {
while(file = root.openNextFile()) {
const char *fn = file.name();
if (!strncmp(fn, "/DATA/", 6)) {
fn += 6;
unsigned int size = file.size();
Serial.print(fn);
Serial.print(' ');
Serial.print(size);
Serial.println(" bytes");
unsigned int id = atoi(fn);
if (id) {
n += snprintf(buf + n, bufsize - n, "{\"id\":%u,\"size\":%u",
id, size);
if (id == fileid) {
n += snprintf(buf + n, bufsize - n, ",\"active\":true");
}
n += snprintf(buf + n, bufsize - n, "},");
}
}
}
if (buf[n - 1] == ',') n--;
}
n += snprintf(buf + n, bufsize - n, "]");
param->contentType=HTTPFILETYPE_JSON;
param->contentLength = n;
return FLAG_DATA_RAW;
}
int handlerLogDelete(UrlHandlerParam* param)
{
int id = 0;
if (param->pucRequest[0] == '/') {
id = atoi(param->pucRequest + 1);
}
sprintf(param->pucBuffer, "/DATA/%u.CSV", id);
if (id == fileid) {
strcat(param->pucBuffer, " still active");
} else {
#if STORAGE == STORAGE_SPIFFS
bool removal = SPIFFS.remove(param->pucBuffer);
#else
bool removal = SD.remove(param->pucBuffer);
#endif
if (removal) {
strcat(param->pucBuffer, " deleted");
} else {
strcat(param->pucBuffer, " not found");
}
}
param->contentLength = strlen(param->pucBuffer);
param->contentType = HTTPFILETYPE_TEXT;
return FLAG_DATA_RAW;
}
UrlHandler urlHandlerList[]={
{"api/live", handlerLiveData},
{"api/info", handlerInfo},
#if STORAGE != STORAGE_NONE
{"api/list", handlerLogList},
{"api/data", handlerLogData},
{"api/log", handlerLogFile},
{"api/delete", handlerLogDelete},
#endif
{0}
};
void obtainTime()
{
sntp_setoperatingmode(SNTP_OPMODE_POLL);
sntp_setservername(0, (char*)"pool.ntp.org");
sntp_init();
}
void serverProcess(int timeout)
{
mwHttpLoop(&httpParam, timeout);
}
bool serverSetup(IPAddress& ip)
{
#if NET_DEVICE == NET_WIFI
WiFi.mode (WIFI_AP_STA);
#else
WiFi.mode (WIFI_AP);
#endif
WiFi.softAP(WIFI_AP_SSID, WIFI_AP_PASSWORD);
ip = WiFi.softAPIP();
mwInitParam(&httpParam, 80, "/spiffs");
httpParam.pxUrlHandler = urlHandlerList;
httpParam.maxClients = 4;
if (mwServerStart(&httpParam)) {
return false;
}
#if NET_DEVICE == NET_WIFI
obtainTime();
#endif
return true;
}
#else
void serverProcess(int timeout)
{
delay(timeout);
}
#endif

View File

@ -0,0 +1,34 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; http://docs.platformio.org/page/projectconf.html
[env:esp32dev]
platform = espressif32
; build_flags = -DBOARD_HAS_PSRAM
; build_flags = -DCORE_DEBUG_LEVEL=5 -DSTACK_SIZE=16384
board=esp-wrover-kit
board_build.f_cpu = 160000000L
framework = arduino
monitor_speed = 115200
board_build.flash_mode = qio
board_build.partitions = huge_app.csv
;[env:esp32c3]
;platform = espressif32
;framework = arduino
;board = esp32-c3-devkitm-1
;board_build.mcu = esp32c3
;monitor_speed=115200
;board_build.partitions = huge_app.csv
[platformio]
src_dir=.
[env]
lib_extra_dirs=../../libraries

View File

@ -0,0 +1,737 @@
/******************************************************************************
* Freematics Hub client and Traccar client implementations
* Works with Freematics ONE+
* Developed by Stanley Huang <stanley@freematics.com.au>
* Distributed under BSD license
* Visit https://freematics.com/products for hardware information
* Visit https://hub.freematics.com to view live and history telemetry data
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#include <FreematicsPlus.h>
#include "telestore.h"
#include "telemesh.h"
#include "teleclient.h"
#include "config.h"
#if SERVER_ENCRYPTION_ENABLE == 1
#include "telecrypt.h"
#endif
extern int16_t rssi;
extern char devid[];
extern char vin[];
extern GPS_DATA* gd;
extern char isoTime[];
CBuffer::CBuffer(uint8_t* mem)
{
m_data = mem;
purge();
}
void CBuffer::add(uint16_t pid, uint8_t type, void* values, int bytes, uint8_t count)
{
if (offset < BUFFER_LENGTH - sizeof(ELEMENT_HEAD) - bytes) {
ELEMENT_HEAD hdr = {pid, type, count};
*(ELEMENT_HEAD*)(m_data + offset) = hdr;
offset += sizeof(ELEMENT_HEAD);
memcpy(m_data + offset, values, bytes);
offset += bytes;
total++;
} else {
Serial.println("FULL");
}
}
void CBuffer::purge()
{
state = BUFFER_STATE_EMPTY;
timestamp = 0;
offset = 0;
total = 0;
}
void CBuffer::serialize(CStorage& store)
{
uint16_t of = 0;
for (int n = 0; n < total && of < offset; n++) {
ELEMENT_HEAD* hdr = (ELEMENT_HEAD*)(m_data + of);
of += sizeof(ELEMENT_HEAD);
switch (hdr->type) {
case ELEMENT_UINT8:
store.log(hdr->pid, (uint8_t*)(m_data + of), hdr->count);
of += (uint16_t)hdr->count * sizeof(uint8_t);
break;
case ELEMENT_UINT16:
store.log(hdr->pid, (uint16_t*)(m_data + of), hdr->count);
of += (uint16_t)hdr->count * sizeof(uint16_t);
break;
case ELEMENT_UINT32:
store.log(hdr->pid, (uint32_t*)(m_data + of), hdr->count);
of += (uint16_t)hdr->count * sizeof(uint32_t);
break;
case ELEMENT_INT32:
store.log(hdr->pid, (int32_t*)(m_data + of), hdr->count);
of += (uint16_t)hdr->count * sizeof(int32_t);
break;
case ELEMENT_FLOAT:
store.log(hdr->pid, (float*)(m_data + of), hdr->count);
of += (uint16_t)hdr->count * sizeof(float);
break;
case ELEMENT_FLOAT_D1:
store.log(hdr->pid, (float*)(m_data + of), hdr->count, "%.1f");
of += (uint16_t)hdr->count * sizeof(float);
break;
case ELEMENT_FLOAT_D2:
store.log(hdr->pid, (float*)(m_data + of), hdr->count, "%.2f");
of += (uint16_t)hdr->count * sizeof(float);
break;
default:
return;
}
}
}
void CBufferManager::init()
{
total = BUFFER_SLOTS;
#if BOARD_HAS_PSRAM
slots = (CBuffer**)heap_caps_malloc(BUFFER_SLOTS * sizeof(void*), MALLOC_CAP_SPIRAM);
#else
slots = (CBuffer**)malloc(BUFFER_SLOTS * sizeof(void*));
#endif
for (int n = 0; n < BUFFER_SLOTS; n++) {
void* mem;
#if BOARD_HAS_PSRAM
mem = heap_caps_malloc(BUFFER_LENGTH, MALLOC_CAP_SPIRAM);
#else
mem = malloc(BUFFER_LENGTH);
#endif
if (!mem) {
Serial.println("OUT OF RAM");
total = n;
break;
}
slots[n] = new CBuffer((uint8_t*)mem);
}
assert(total > 0);
}
void CBufferManager::purge()
{
for (int n = 0; n < total; n++) slots[n]->purge();
}
CBuffer* CBufferManager::getFree()
{
if (last) {
CBuffer* slot = last;
last = 0;
if (slot->state == BUFFER_STATE_EMPTY) return slot;
}
uint32_t ts = 0xffffffff;
int m = 0;
// search for free slot, if none, mark the oldest one
for (int n = 0; n < total; n++) {
if (slots[n]->state == BUFFER_STATE_EMPTY) {
return slots[n];
} else if (slots[n]->state == BUFFER_STATE_FILLED && slots[n]->timestamp < ts) {
m = n;
ts = slots[n]->timestamp;
}
}
// dispose oldest data when buffer is full
while (slots[m]->state == BUFFER_STATE_LOCKED) delay(1);
slots[m]->purge();
return slots[m];
}
CBuffer* CBufferManager::getOldest()
{
uint32_t ts = 0xffffffff;
int m = -1;
for (int n = 0; n < total; n++) {
if (slots[n]->state == BUFFER_STATE_FILLED && slots[n]->timestamp < ts) {
m = n;
ts = slots[n]->timestamp;
}
}
if (m >= 0) {
slots[m]->state = BUFFER_STATE_LOCKED;
return slots[m];
}
return 0;
}
CBuffer* CBufferManager::getNewest()
{
uint32_t ts = 0;
int m = -1;
for (int n = 0; n < total; n++) {
if (slots[n]->state == BUFFER_STATE_FILLED && slots[n]->timestamp > ts) {
m = n;
ts = slots[n]->timestamp;
}
}
if (m >= 0) {
slots[m]->state = BUFFER_STATE_LOCKED;
return slots[m];
}
return 0;
}
void CBufferManager::free(CBuffer* slot)
{
slot->purge();
last = slot;
}
void CBufferManager::printStats()
{
int bytes = 0;
int count = 0;
int samples = 0;
for (int n = 0; n < total; n++) {
if (slots[n]->state != BUFFER_STATE_FILLED) continue;
bytes += slots[n]->offset;
samples += slots[n]->total;
count++;
}
if (slots) {
Serial.print("[BUF] ");
Serial.print(samples);
Serial.print(" samples | ");
Serial.print(bytes);
Serial.print(" bytes | ");
Serial.print(count);
Serial.print('/');
Serial.println(total);
}
}
bool TeleClientUDP::verifyChecksum(char* data)
{
uint8_t sum = 0;
char *s = strrchr(data, '*');
if (!s) return false;
for (char *p = data; p < s; p++) sum += *p;
if (hex2uint8(s + 1) == sum) {
*s = 0;
return true;
}
return false;
}
bool TeleClientUDP::notify(byte event, const char* payload)
{
char buf[48];
char cache[128];
CStorageRAM netbuf;
netbuf.init(cache, 128);
netbuf.header(devid);
netbuf.dispatch(buf, sprintf(buf, "EV=%X", (unsigned int)event));
netbuf.dispatch(buf, sprintf(buf, "TS=%lu", millis()));
netbuf.dispatch(buf, sprintf(buf, "ID=%s", devid));
if (rssi) {
netbuf.dispatch(buf, sprintf(buf, "SSI=%d", (int)rssi));
}
if (vin[0]) {
netbuf.dispatch(buf, sprintf(buf, "VIN=%s", vin));
}
if (payload) {
netbuf.dispatch(payload, strlen(payload));
}
netbuf.tailer();
//Serial.println(netbuf.buffer());
for (byte attempts = 0; attempts < 3; attempts++) {
// send notification datagram
#if ENABLE_WIFI
if (wifi.connected())
{
#if SERVER_ENCRYPTION_ENABLE == 1
char *orig_send_buf = netbuf.buffer();
unsigned int orig_send_buf_len = netbuf.length();
unsigned char encrypted_buf[12 + orig_send_buf_len + 16];
encrypt_string((unsigned char *)orig_send_buf, orig_send_buf_len, encrypted_buf);
if (!wifi.send((const char *)encrypted_buf, sizeof(encrypted_buf))) break;
#else
if (!wifi.send(netbuf.buffer(), netbuf.length())) break;
#endif
}
else
#endif
{
#if SERVER_ENCRYPTION_ENABLE == 1
char *orig_send_buf = netbuf.buffer();
unsigned int orig_send_buf_len = netbuf.length();
unsigned char encrypted_buf[12 + orig_send_buf_len + 16];
encrypt_string((unsigned char *)orig_send_buf, orig_send_buf_len, encrypted_buf);
if (!cell.send((const char *)encrypted_buf, sizeof(encrypted_buf))) break;
#else
if (!cell.send(netbuf.buffer(), netbuf.length())) break;
#endif
}
if (event == EVENT_ACK) return true; // no reply for ACK
char *data = 0;
int bytesRecv = 0;
// receive reply
#if ENABLE_WIFI
if (wifi.connected())
{
data = cell.getBuffer();
bytesRecv = wifi.receive(data, RECV_BUF_SIZE - 1);
if (bytesRecv > 0) {
data[bytesRecv] = 0;
}
}
else
#endif
{
data = cell.receive(&bytesRecv);
}
if (!data || bytesRecv == 0) {
Serial.println("[UDP] Timeout");
continue;
}
rxBytes += bytesRecv;
// decrypt received data
#if SERVER_ENCRYPTION_ENABLE == 1
Serial.println("decrypting data");
if (bytesRecv >= 12 + 16) {
char decrypted_data[bytesRecv - 12 - 16 + 1]; // +1 for null-terminator
decrypt_string((unsigned char *)data, bytesRecv, (unsigned char *)decrypted_data);
Serial.println("decrytion function exited");
if (decrypted_data[0] == '\0') {
continue;
}
data = decrypted_data;
bytesRecv = strlen(decrypted_data);
} else {
Serial.println("[CHACHA] Received data is too short to be decrypted");
continue;
}
Serial.println("decrypted data");
#endif
// verify checksum
if (!verifyChecksum(data)) {
Serial.print("[UDP] Checksum mismatch:");
Serial.println(data);
continue;
}
char pattern[16];
sprintf(pattern, "EV=%u", event);
if (!strstr(data, pattern)) {
Serial.print("[UDP] Invalid reply: ");
Serial.println(data);
continue;
}
if (event == EVENT_LOGIN) {
// extract info from server response
char *p = strstr(data, "TM=");
if (p) {
// set local time from server
unsigned long tm = atol(p + 3);
struct timeval tv = { .tv_sec = (time_t)tm, .tv_usec = 0 };
settimeofday(&tv, NULL);
}
p = strstr(data, "SN=");
if (p) {
char *q = strchr(p, ',');
if (q) *q = 0;
}
feedid = hex2uint16(data);
login = true;
} else if (event == EVENT_LOGOUT) {
login = false;
}
// success
return true;
}
return false;
}
bool TeleClientUDP::connect(bool quick)
{
byte event = login ? EVENT_RECONNECT : EVENT_LOGIN;
bool success = false;
#if ENABLE_WIFI
if (wifi.connected())
{
if (quick) return wifi.open(SERVER_HOST, SERVER_PORT);
}
else
#endif
{
cell.close();
if (quick) {
return cell.open(0, 0);
}
}
packets = 0;
// connect to telematics server
for (byte attempts = 0; attempts < 3; attempts++) {
Serial.print(event == EVENT_LOGIN ? "LOGIN(" : "RECONNECT(");
Serial.print(SERVER_HOST);
Serial.print(':');
Serial.print(SERVER_PORT);
Serial.println(")...");
#if ENABLE_WIFI
if (wifi.connected())
{
if (!wifi.open(SERVER_HOST, SERVER_PORT)) {
Serial.println("[WIFI] Unable to connect");
delay(1000);
continue;
}
}
else
#endif
{
if (!cell.open(SERVER_HOST, SERVER_PORT)) {
if (!cell.check()) break;
Serial.println("[NET] Unable to connect");
delay(3000);
continue;
}
}
// log in or reconnect to Freematics Hub
if (!notify(event)) {
#if ENABLE_WIFI
if (wifi.connected())
{
wifi.close();
}
else
#endif
{
if (!cell.check()) break;
cell.close();
}
Serial.println("[NET] Server timeout");
continue;
}
success = true;
break;
}
if (event == EVENT_LOGIN) startTime = millis();
if (success) {
lastSyncTime = millis();
}
return success;
}
bool TeleClientUDP::ping()
{
bool success = false;
for (byte n = 0; n < 3 && !success; n++) {
#if ENABLE_WIFI
if (wifi.connected())
{
success = wifi.open(SERVER_HOST, SERVER_PORT);
}
else
#endif
{
success = cell.open(SERVER_HOST, SERVER_PORT);
}
if (success) {
if ((success = notify(EVENT_PING))) break;
#if ENABLE_WIFI
if (wifi.connected())
{
wifi.close();
}
else
#endif
{
cell.close();
}
delay(1000);
}
}
if (success) lastSyncTime = millis();
return success;
}
bool TeleClientUDP::transmit(const char* packetBuffer, unsigned int packetSize)
{
#if ENABLE_WIFI
// transmit data via wifi
if (wifi.connected()) {
if (wifi.send(packetBuffer, packetSize)) {
txBytes += packetSize;
txCount++;
Serial.print("[WIFI] ");
Serial.print(packetSize);
Serial.println(" bytes sent");
return true;
}
return false;
}
#endif
// transmit data via cellular
if (++packets >= 64) {
cell.close();
cell.open(0, 0);
packets = 0;
}
Serial.print("[CELL] ");
Serial.print(packetSize);
Serial.println(" bytes being sent");
if (cell.send(packetBuffer, packetSize)) {
txBytes += packetSize;
txCount++;
return true;
}
return false;
}
void TeleClientUDP::inbound()
{
// check incoming datagram
do {
int len = 0;
char *data = 0;
#if ENABLE_WIFI
if (wifi.connected())
{
data = cell.getBuffer();
len = wifi.receive(data, RECV_BUF_SIZE - 1, 10);
}
else
#endif
{
data = cell.receive(&len, 50);
}
if (!data || len == 0) break;
data[len] = 0;
Serial.print("[UDP] ");
Serial.println(data);
rxBytes += len;
if (!verifyChecksum(data)) {
Serial.print("[UDP] Checksum mismatch:");
Serial.println(data);
break;
}
char *p = strstr(data, "EV=");
if (!p) break;
int eventID = atoi(p + 3);
switch (eventID) {
case EVENT_SYNC:
feedid = hex2uint16(data);
Serial.print("[UDP] FEED ID:");
Serial.println(feedid);
break;
}
lastSyncTime = millis();
} while(0);
}
void TeleClientUDP::shutdown()
{
if (login) {
notify(EVENT_LOGOUT);
login = false;
Serial.println("[NET] Logout");
}
#if ENABLE_WIFI
if (wifi.connected()) {
wifi.end();
Serial.println("[WIFI] Deactivated");
return;
}
#endif
cell.end();
Serial.println("[CELL] Deactivated");
}
bool TeleClientHTTP::notify(byte event, const char* payload)
{
char url[256];
snprintf(url, sizeof(url), "%s/notify/%s?EV=%u&SSI=%d&VIN=%s", SERVER_PATH, devid,
(unsigned int)event, (int)rssi, vin);
if (event == EVENT_LOGOUT) login = false;
#if ENABLE_WIFI
if (wifi.connected())
{
return wifi.send(METHOD_GET, url, true) && wifi.receive(cell.getBuffer(), RECV_BUF_SIZE - 1) && wifi.code() == 200;
}
else
#endif
{
return cell.send(METHOD_GET, url, true) && cell.receive() && cell.code() == 200;
}
}
bool TeleClientHTTP::transmit(const char* packetBuffer, unsigned int packetSize)
{
#if ENABLE_WIFI
if ((wifi.connected() && wifi.state() != HTTP_CONNECTED) || cell.state() != HTTP_CONNECTED) {
#else
if (cell.state() != HTTP_CONNECTED) {
#endif
// reconnect if disconnected
if (!connect(true)) {
return false;
}
}
char url[256];
bool success = false;
int len;
#if SERVER_METHOD == PROTOCOL_METHOD_GET
if (gd && gd->ts) {
len = snprintf(url, sizeof(url), "%s/push?id=%s&timestamp=%s&lat=%f&lon=%f&altitude=%d&speed=%f&heading=%d",
SERVER_PATH, devid, isoTime,
gd->lat, gd->lng, (int)gd->alt, gd->speed, (int)gd->heading);
} else {
len = snprintf(url, sizeof(url), "%s/push?id=%s", SERVER_PATH, devid);
}
success = cell.send(METHOD_GET, url, true);
#else
len = snprintf(url, sizeof(url), "%s/post/%s", SERVER_PATH, devid);
#if ENABLE_WIFI
if (wifi.connected()) {
Serial.print("[WIFI] ");
Serial.println(url);
success = wifi.send(METHOD_POST, url, true, packetBuffer, packetSize);
}
else
#endif
{
Serial.print("[CELL] ");
Serial.println(url);
success = cell.send(METHOD_POST, url, true, packetBuffer, packetSize);
}
len += packetSize;
#endif
if (!success) {
Serial.println("Connection closed");
return false;
} else {
txBytes += len;
txCount++;
}
// check response
int recvBytes = 0;
char* content = 0;
#if ENABLE_WIFI
if (wifi.connected())
{
content = wifi.receive(cell.getBuffer(), RECV_BUF_SIZE - 1, &recvBytes);
}
else
#endif
{
content = cell.receive(&recvBytes);
}
if (!content) {
// close connection on receiving timeout
Serial.println("No HTTP response");
return false;
}
Serial.print("[HTTP] ");
Serial.println(content);
#if ENABLE_WIFI
if ((wifi.connected() && wifi.code() == 200) || cell.code() == 200) {
#else
if (cell.code() == 200) {
#endif
// successful
lastSyncTime = millis();
rxBytes += recvBytes;
}
return true;
}
bool TeleClientHTTP::connect(bool quick)
{
if (!quick) {
#if ENABLE_WIFI
if (!wifi.connected()) cell.init();
#else
cell.init();
#endif
} else {
#if ENABLE_WIFI
if (!wifi.connected()) cell.close();
#else
cell.close();
#endif
}
// connect to HTTP server
bool success = false;
#if ENABLE_WIFI
if (wifi.connected()) success = wifi.open(SERVER_HOST, SERVER_PORT);
#endif
if (!success) {
for (byte attempts = 0; !success && attempts < 3; attempts++) {
success = cell.open(SERVER_HOST, SERVER_PORT);
if (!success) {
if (!cell.check()) break;
cell.close();
cell.init();
}
}
}
if (!success) {
Serial.println("[CELL] Unable to connect");
return false;
}
if (quick) return true;
if (!login) {
Serial.print("LOGIN(");
Serial.print(SERVER_HOST);
Serial.print(':');
Serial.print(SERVER_PORT);
Serial.println(")...");
// log in or reconnect to Freematics Hub
if (notify(EVENT_LOGIN)) {
lastSyncTime = millis();
login = true;
}
}
return true;
}
bool TeleClientHTTP::ping()
{
return connect();
}
void TeleClientHTTP::shutdown()
{
if (login) {
notify(EVENT_LOGOUT);
login = false;
Serial.println("[NET] Logout");
}
#if ENABLE_WIFI
if (wifi.connected()) {
wifi.end();
Serial.println("[WIFI] Deactivated");
return;
}
#endif
cell.close();
cell.end();
Serial.println("[CELL] Deactivated");
}

View File

@ -0,0 +1,114 @@
#include "config.h"
#define EVENT_LOGIN 1
#define EVENT_LOGOUT 2
#define EVENT_SYNC 3
#define EVENT_RECONNECT 4
#define EVENT_COMMAND 5
#define EVENT_ACK 6
#define EVENT_PING 7
#define BUFFER_STATE_EMPTY 0
#define BUFFER_STATE_FILLING 1
#define BUFFER_STATE_FILLED 2
#define BUFFER_STATE_LOCKED 3
#define ELEMENT_UINT8 0
#define ELEMENT_UINT16 1
#define ELEMENT_UINT32 2
#define ELEMENT_INT32 3
#define ELEMENT_FLOAT 4
#define ELEMENT_FLOAT_D1 5 /* floating-point data with 1 decimal place*/
#define ELEMENT_FLOAT_D2 6 /* floating-point data with 2 decimal places*/
typedef struct {
uint16_t pid;
uint8_t type;
uint8_t count;
} ELEMENT_HEAD;
class CBuffer
{
public:
CBuffer(uint8_t* mem);
void add(uint16_t pid, uint8_t type, void* values, int bytes, uint8_t count = 1);
void purge();
void serialize(CStorage& store);
uint32_t timestamp;
uint16_t offset;
uint8_t total;
uint8_t state;
private:
uint8_t* m_data;
};
class CBufferManager
{
public:
void init();
void purge();
void free(CBuffer* slot);
CBuffer* getFree();
CBuffer* getOldest();
CBuffer* getNewest();
void printStats();
private:
CBuffer** slots = 0;
CBuffer* last = 0;
uint32_t total = 0;
};
class TeleClient
{
public:
virtual void reset()
{
txCount = 0;
txBytes = 0;
rxBytes = 0;
login = false;
startTime = millis();
}
virtual bool notify(byte event, const char* payload = 0) { return true; }
virtual bool connect() { return true; }
virtual bool transmit(const char* packetBuffer, unsigned int packetSize) { return true; }
virtual void inbound() {}
uint32_t txCount = 0;
uint32_t txBytes = 0;
uint32_t rxBytes = 0;
uint32_t lastSyncTime = 0;
uint16_t feedid = 0;
uint32_t startTime = 0;
uint8_t packets = 0;
bool login = false;
};
class TeleClientUDP : public TeleClient
{
public:
bool notify(byte event, const char* payload = 0);
bool connect(bool quick = false);
bool transmit(const char* packetBuffer, unsigned int packetSize);
bool ping();
void inbound();
bool verifyChecksum(char* data);
void shutdown();
#if ENABLE_WIFI
WifiUDP wifi;
#endif
CellUDP cell;
};
class TeleClientHTTP : public TeleClient
{
public:
bool notify(byte event, const char* payload = 0);
bool connect(bool quick = false);
bool transmit(const char* packetBuffer, unsigned int packetSize);
bool ping();
void shutdown();
#if ENABLE_WIFI
WifiHTTP wifi;
#endif
CellHTTP cell;
};

View File

@ -0,0 +1,120 @@
#include "config.h"
#include <string.h>
#include <stdio.h>
#include <ChaChaPoly.h>
#include <HardwareSerial.h>
void print_hex(const unsigned char *data, size_t length) {
for (size_t i = 0; i < length; ++i) {
Serial.printf("%02x", data[i]);
}
Serial.println();
}
void encrypt_string(const unsigned char *input, size_t length, unsigned char *output) {
// Create an instance of the ChaChaPoly class
ChaChaPoly chachaPoly;
// Initialize the encryption key
unsigned char key[32];
for (int i = 0; i < 32; ++i) {
sscanf(CHACHA20_KEY + 2*i, "%02x", &key[i]);
}
// Set the encryption key
chachaPoly.setKey(key, sizeof(key));
// Generate a random nonce (IV)
unsigned char nonce[12];
esp_fill_random(nonce, sizeof(nonce)); // Use the ESP-IDF random number generator
chachaPoly.setIV(nonce, sizeof(nonce));
// Encrypt the input data
chachaPoly.encrypt(output + sizeof(nonce), input, length);
// Compute the authentication tag
chachaPoly.computeTag(output + sizeof(nonce) + length, chachaPoly.tagSize());
// Prepend the nonce to the output
memcpy(output, nonce, sizeof(nonce));
// Clear the encryption context
chachaPoly.clear();
}
void decrypt_string(const unsigned char *input, size_t length, unsigned char *output) {
// Create an instance of the ChaChaPoly class
ChaChaPoly chachaPoly;
// Initialize the decryption key
unsigned char key[32];
for (int i = 0; i < 32; ++i) {
sscanf(CHACHA20_KEY + 2*i, "%02x", &key[i]);
}
Serial.println("loaded key");
// Set the decryption key
chachaPoly.setKey(key, sizeof(key));
Serial.println("set key");
// Extract the nonce (IV) from the input
unsigned char nonce[12];
memcpy(nonce, input, sizeof(nonce));
chachaPoly.setIV(nonce, sizeof(nonce));
// Check that length is long enough to contain a nonce and a tag.
if (length < sizeof(nonce) + chachaPoly.tagSize()) {
Serial.print("[CHACHA] Input too short to contain nonce and tag: ");
print_hex(input, length);
output[0] = '\0'; // Set output to an empty string
return;
}
Serial.println("did nonce");
// Decrypt the input data
size_t decryptedLength = length - sizeof(nonce) - chachaPoly.tagSize();
chachaPoly.decrypt(output, input + sizeof(nonce), decryptedLength);
Serial.println("did decryption");
// Print the decrypted data as hex values
String decryptedString = "";
for (size_t i = 0; i < decryptedLength; i++) {
decryptedString += (char)output[i];
}
Serial.println(decryptedString);
// Verify the authentication tag
const unsigned char *tagPtr = input + sizeof(nonce) + decryptedLength;
Serial.print("Tag: ");
for (size_t i = 0; i < chachaPoly.tagSize(); i++) {
Serial.print(tagPtr[i], HEX);
Serial.print(" ");
}
Serial.println();
Serial.print("Computed Tag: ");
uint8_t computedTag[16];
chachaPoly.computeTag(computedTag, sizeof(computedTag));
for (size_t i = 0; i < sizeof(computedTag); i++) {
Serial.print(computedTag[i], HEX);
Serial.print(" ");
}
Serial.println();
if (!chachaPoly.checkTag(tagPtr, chachaPoly.tagSize())) {
Serial.println("Authentication failed!");
output[0] = '\0'; // Set output to an empty string
return;
}
///
// Clear the decryption context
chachaPoly.clear();
}

View File

@ -0,0 +1,9 @@
#include "config.h"
#include <string.h>
#include <stdio.h>
#include <ChaChaPoly.h>
#include <HardwareSerial.h>
void encrypt_string(const unsigned char *input, size_t length, unsigned char *output);
void decrypt_string(const unsigned char *input, size_t length, unsigned char *output);
void print_hex(const unsigned char *data, size_t length);

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,113 @@
#include "FreematicsPlus.h"
#include "config.h"
#include "telemesh.h"
#if 0
#include <mdf_common.h>
#include <mwifi.h>
static mdf_err_t event_loop_cb(mdf_event_loop_t event, void *ctx)
{
switch (event) {
case MDF_EVENT_MWIFI_STARTED:
Serial.println("MESH is started");
break;
case MDF_EVENT_MWIFI_PARENT_CONNECTED:
Serial.println("Parent is connected on station interface");
break;
case MDF_EVENT_MWIFI_PARENT_DISCONNECTED:
Serial.println("Parent is disconnected on station interface");
break;
default:
break;
}
return MDF_OK;
}
static mdf_err_t wifi_init()
{
mdf_err_t ret = nvs_flash_init();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
nvs_flash_erase();
ret = nvs_flash_init();
}
//MDF_ERROR_ASSERT(ret);
tcpip_adapter_init();
esp_event_loop_init(NULL, NULL);
esp_wifi_init(&cfg);
esp_wifi_set_storage(WIFI_STORAGE_FLASH);
esp_wifi_set_mode(WIFI_MODE_STA);
esp_wifi_set_ps(WIFI_PS_NONE);
esp_mesh_set_6m_rate(false);
return esp_wifi_start();
}
bool ClientWiFiMesh::begin(CFreematics* device)
{
if (m_inited) return true;
mwifi_init_config_t cfg = MWIFI_INIT_CONFIG_DEFAULT();
mwifi_config_t config = {0};
config.channel = WIFI_MESH_CHANNEL;
config.mesh_type = MWIFI_MESH_NODE;
memcpy(config.mesh_id, WIFI_MESH_ID, sizeof(WIFI_MESH_ID) - 1);
mdf_event_loop_init(event_loop_cb);
wifi_init();
mwifi_init(&cfg);
mwifi_set_config(&config);
if (mwifi_start() == MDF_OK) {
m_inited = true;
return true;
}
return false;
}
bool ClientWiFiMesh::open(const char* host, uint16_t port)
{
return mwifi_is_connected();
}
bool ClientWiFiMesh::send(const char* data, unsigned int len)
{
if (mwifi_is_connected()) {
mwifi_data_type_t data_type = {0x0};
if (mwifi_write(NULL, &data_type, data, len, true) == MDF_OK)
return true;
}
return false;
}
char* ClientWiFiMesh::receive(int* pbytes, unsigned int timeout)
{
if (mwifi_is_connected()) {
uint8_t src_addr[MWIFI_ADDR_LEN] = {0x0};
mwifi_data_type_t data_type = {0x0};
size_t size = MESH_RECV_BUF_SIZE - 1;
if (mwifi_read(src_addr, &data_type, m_buffer, &size, timeout / portTICK_PERIOD_MS) == MDF_OK) {
m_buffer[size] = 0;
if (pbytes) *pbytes = size;
return m_buffer;
}
}
return 0;
}
ClientWiFiMesh::ClientWiFiMesh()
{
m_buffer = (char*)malloc(MESH_RECV_BUF_SIZE);
}
ClientWiFiMesh::~ClientWiFiMesh()
{
free(m_buffer);
}
#endif

View File

@ -0,0 +1,43 @@
class ClientSerial
{
public:
bool begin(CFreematics* device) { return true; }
void end() {}
bool open(const char* host, uint16_t port) { return true; }
void close() {}
bool send(const char* data, unsigned int len)
{
Serial.write((uint8_t*)data, len);
Serial.println();
return true;
}
char* receive(int* pbytes = 0, unsigned int timeout = 5000)
{
Serial.setTimeout(timeout);
int bytes = Serial.readBytes((uint8_t*)m_buffer, sizeof(m_buffer) - 1);
if (pbytes) *pbytes = bytes;
return bytes > 0 ? m_buffer : 0;
}
const char* deviceName() { return "Serial"; }
private:
char m_buffer[128] = {0};
};
#define MESH_RECV_BUF_SIZE 256
class ClientWiFiMesh
{
public:
ClientWiFiMesh();
~ClientWiFiMesh();
bool begin(CFreematics* device);
void end() {}
bool open(const char* host, uint16_t port);
void close() {}
bool send(const char* data, unsigned int len);
char* receive(int* pbytes = 0, unsigned int timeout = 5000);
const char* deviceName() { return "WiFi Mesh"; }
private:
char* m_buffer;
bool m_inited = false;
};

View File

@ -0,0 +1,263 @@
#include <FreematicsPlus.h>
#include "telestore.h"
void CStorage::log(uint16_t pid, uint8_t values[], uint8_t count)
{
char buf[256];
byte n = snprintf(buf, sizeof(buf), "%X%c%u", pid, m_delimiter, (unsigned int)values[0]);
for (byte m = 1; m < count; m++) {
n += snprintf(buf + n, sizeof(buf) - n, ";%u", (unsigned int)values[m]);
}
dispatch(buf, n);
}
void CStorage::log(uint16_t pid, uint16_t values[], uint8_t count)
{
char buf[256];
byte n = snprintf(buf, sizeof(buf), "%X%c%u", pid, m_delimiter, (unsigned int)values[0]);
for (byte m = 1; m < count; m++) {
n += snprintf(buf + n, sizeof(buf) - n, ";%u", (unsigned int)values[m]);
}
dispatch(buf, n);
}
void CStorage::log(uint16_t pid, uint32_t values[], uint8_t count)
{
char buf[256];
byte n = snprintf(buf, sizeof(buf), "%X%c%u", pid, m_delimiter, values[0]);
for (byte m = 1; m < count; m++) {
n += snprintf(buf + n, sizeof(buf) - n, ";%u", values[m]);
}
dispatch(buf, n);
}
void CStorage::log(uint16_t pid, int32_t values[], uint8_t count)
{
char buf[256];
byte n = snprintf(buf, sizeof(buf), "%X%c%d", pid, m_delimiter, values[0]);
for (byte m = 1; m < count; m++) {
n += snprintf(buf + n, sizeof(buf) - n, ";%d", values[m]);
}
dispatch(buf, n);
}
void CStorage::log(uint16_t pid, float values[], uint8_t count, const char* fmt)
{
char buf[256];
char *p = buf + snprintf(buf, sizeof(buf), "%X%c", pid, m_delimiter);
for (byte m = 0; m < count && (p - buf) < sizeof(buf) - 3; m++) {
if (m > 0) *(p++) = ';';
int l = snprintf(p, sizeof(buf) - (p - buf), fmt, values[m]);
char *q = strchr(p, '.');
if (q && atoi(q + 1) == 0) {
*q = 0;
if (*p == '-' && *(p + 1) == '0') {
*p = '0';
*(++p) = 0;
} else {
p = q;
}
} else {
p += l;
}
}
dispatch(buf, (int)(p - buf));
}
void CStorage::timestamp(uint32_t ts)
{
log(PID_TIMESTAMP, &ts, 1);
}
void CStorage::dispatch(const char* buf, byte len)
{
// output data via serial
Serial.write((uint8_t*)buf, len);
Serial.write(' ');
m_samples++;
}
byte CStorage::checksum(const char* data, int len)
{
byte sum = 0;
for (int i = 0; i < len; i++) sum += data[i];
return sum;
}
void CStorageRAM::dispatch(const char* buf, byte len)
{
// reserve some space for checksum
int remain = m_cacheSize - m_cacheBytes - len - 3;
if (remain < 0) {
// m_cache full
return;
}
// store data in m_cache
memcpy(m_cache + m_cacheBytes, buf, len);
m_cacheBytes += len;
m_cache[m_cacheBytes++] = ',';
m_samples++;
}
void CStorageRAM::header(const char* devid)
{
m_cacheBytes = sprintf(m_cache, "%s#", devid);
}
void CStorageRAM::tailer()
{
if (m_cache[m_cacheBytes - 1] == ',') m_cacheBytes--;
m_cacheBytes += sprintf(m_cache + m_cacheBytes, "*%X", (unsigned int)checksum(m_cache, m_cacheBytes));
}
void CStorageRAM::untailer()
{
char *p = strrchr(m_cache, '*');
if (p) {
*p = ',';
m_cacheBytes = p + 1 - m_cache;
}
}
void FileLogger::dispatch(const char* buf, byte len)
{
if (m_id == 0) return;
if (m_file.write((uint8_t*)buf, len) != len) {
// try again
if (m_file.write((uint8_t*)buf, len) != len) {
Serial.println("Error writing. End file logging.");
end();
return;
}
}
m_file.write('\n');
m_size += (len + 1);
}
int FileLogger::getFileID(File& root)
{
if (root) {
File file;
int id = 0;
while(file = root.openNextFile()) {
char *p = strrchr(file.name(), '/');
unsigned int n = atoi(p ? p + 1 : file.name());
if (n > id) id = n;
}
return id + 1;
} else {
return 0;
}
}
bool SDLogger::init()
{
SPI.begin();
if (SD.begin(PIN_SD_CS, SPI, SPI_FREQ)) {
unsigned int total = SD.totalBytes() >> 20;
unsigned int used = SD.usedBytes() >> 20;
Serial.print("SD:");
Serial.print(total);
Serial.print(" MB total, ");
Serial.print(used);
Serial.println(" MB used");
return true;
} else {
Serial.println("NO SD CARD");
return false;
}
}
uint32_t SDLogger::begin()
{
File root = SD.open("/DATA");
m_id = getFileID(root);
if (m_id == 0) {
SD.mkdir("/DATA");
m_id = 1;
}
char path[24];
sprintf(path, "/DATA/%u.CSV", m_id);
Serial.print("File: ");
Serial.println(path);
m_file = SD.open(path, FILE_WRITE);
if (!m_file) {
Serial.println("File error");
m_id = 0;
}
m_dataCount = 0;
return m_id;
}
void SDLogger::flush()
{
char path[24];
sprintf(path, "/DATA/%u.CSV", m_id);
m_file.close();
m_file = SD.open(path, FILE_APPEND);
if (!m_file) {
Serial.println("File error");
}
}
bool SPIFFSLogger::init()
{
bool mounted = SPIFFS.begin();
if (!mounted) {
Serial.println("Formatting SPIFFS...");
mounted = SPIFFS.begin(true);
}
if (mounted) {
Serial.print("SPIFFS:");
Serial.print(SPIFFS.totalBytes());
Serial.print(" bytes total, ");
Serial.print(SPIFFS.usedBytes());
Serial.println(" bytes used");
} else {
Serial.println("No SPIFFS");
}
return mounted;
}
uint32_t SPIFFSLogger::begin()
{
File root = SPIFFS.open("/");
m_id = getFileID(root);
char path[24];
sprintf(path, "/DATA/%u.CSV", m_id);
Serial.print("File: ");
Serial.println(path);
m_file = SPIFFS.open(path, FILE_WRITE);
if (!m_file) {
Serial.println("File error");
m_id = 0;
}
m_dataCount = 0;
return m_id;
}
void SPIFFSLogger::purge()
{
// remove oldest file when unused space is insufficient
File root = SPIFFS.open("/");
File file;
int idx = 0;
while(file = root.openNextFile()) {
if (!strncmp(file.name(), "/DATA/", 6)) {
unsigned int n = atoi(file.name() + 6);
if (n != 0 && (idx == 0 || n < idx)) idx = n;
}
}
if (idx) {
m_file.close();
char path[32];
sprintf(path, "/DATA/%u.CSV", idx);
SPIFFS.remove(path);
Serial.print(path);
Serial.println(" removed");
sprintf(path, "/DATA/%u.CSV", m_id);
m_file = SPIFFS.open(path, FILE_APPEND);
if (!m_file) m_id = 0;
}
}

View File

@ -0,0 +1,94 @@
#include <SPI.h>
#include <FS.h>
#include <SD.h>
#include <SPIFFS.h>
class CStorage;
class CStorage {
public:
virtual bool init() { return true; }
virtual void uninit() {}
virtual void log(uint16_t pid, uint8_t values[], uint8_t count);
virtual void log(uint16_t pid, uint16_t values[], uint8_t count);
virtual void log(uint16_t pid, uint32_t values[], uint8_t count);
virtual void log(uint16_t pid, int32_t values[], uint8_t count);
virtual void log(uint16_t pid, float values[], uint8_t count, const char* fmt = "%f");
virtual void timestamp(uint32_t ts);
virtual void purge() { m_samples = 0; }
virtual uint16_t samples() { return m_samples; }
virtual void dispatch(const char* buf, byte len);
protected:
byte checksum(const char* data, int len);
virtual void header(const char* devid) {}
virtual void tailer() {}
int m_samples = 0;
char m_delimiter = ':';
};
class CStorageRAM: public CStorage {
public:
void init(char* cache, unsigned int cacheSize)
{
m_cacheSize = cacheSize;
m_cache = cache;
}
void uninit()
{
if (m_cache) {
delete m_cache;
m_cache = 0;
m_cacheSize = 0;
}
}
void purge() { m_cacheBytes = 0; m_samples = 0; }
unsigned int length() { return m_cacheBytes; }
char* buffer() { return m_cache; }
void dispatch(const char* buf, byte len);
void header(const char* devid);
void tailer();
void untailer();
protected:
unsigned int m_cacheSize = 0;
unsigned int m_cacheBytes = 0;
char* m_cache = 0;
};
class FileLogger : public CStorage {
public:
FileLogger() { m_delimiter = ','; }
virtual void dispatch(const char* buf, byte len);
virtual uint32_t size() { return m_size; }
virtual void end()
{
m_file.close();
m_id = 0;
m_size = 0;
}
virtual void flush()
{
m_file.flush();
}
protected:
int getFileID(File& root);
uint32_t m_dataTime = 0;
uint32_t m_dataCount = 0;
uint32_t m_size = 0;
uint32_t m_id = 0;
File m_file;
};
class SDLogger : public FileLogger {
public:
bool init();
uint32_t begin();
void flush();
};
class SPIFFSLogger : public FileLogger {
public:
bool init();
uint32_t begin();
private:
void purge();
};