134 Commits

Author SHA1 Message Date
367a41527d Updated FW-Version 2023-02-28 10:12:18 +01:00
5460a70f6d Added STL_Files for Case 2023-02-28 10:11:33 +01:00
3b4a22bff7 corrected some DTC-Stuff 2023-02-24 19:28:06 +01:00
df209a788b fixed Debug-Output in WebserverCallback (this time for real) 2023-02-24 19:25:59 +01:00
a6031798da moved DTC Processing to own File 2023-02-24 19:24:26 +01:00
00cba7b5ac DTC-Severity was not correctly set on new DTCs 2023-02-24 19:23:31 +01:00
034b6c918b fixed sysStat-Behaviour 2023-02-24 19:22:51 +01:00
3af678f3f8 fixed DebugOutput in WebserverCallback 2023-02-24 19:22:33 +01:00
32107a45db removed RemoteDebug-Library Stuff 2023-02-24 00:52:51 +01:00
a446a51c07 updated FastLED-Lib 2023-02-24 00:06:31 +01:00
5b41090add reworked debugger enabling 2023-02-24 00:05:51 +01:00
2376d14b5d fixed warning 2023-02-24 00:05:21 +01:00
77a94de2eb new debugger and websockets 2023-02-23 23:14:58 +01:00
c9a6e4c870 fixed warning 2023-02-23 17:46:36 +01:00
9ac161ee4c fixed Display-Bug with Remain Distance 2023-02-23 17:46:28 +01:00
1f8b085598 small improvement of DTC-Handling for CAN 2023-02-20 09:18:14 +01:00
46f98b1244 fixes... 2023-02-19 14:42:40 +01:00
9e87a05418 replaced var-name bc. encoding Issues with special char 2023-02-19 14:29:38 +01:00
140414ee8b minor FormatTweaks for DTC-DebugVal in WebUI 2022-09-02 00:10:21 +02:00
371e21429d WebUI adjusted and prepared EE-Backup-Stuff 2022-09-01 23:46:20 +02:00
64f9e102a5 Disabled autocorrect of Config after SanityCheck 2022-09-01 23:00:19 +02:00
dc4dbb05ca Format-Fixes and DTC-Debgval in WebUI visible 2022-09-01 22:47:25 +02:00
8c0db2ffd9 Implemented DebugVal for DTCs and SanityCheck 2022-09-01 22:46:00 +02:00
c6b47fffaf changed color of Bargraph-Text on WebUI 2022-09-01 21:31:27 +02:00
1b49ed4a2e Sanity-Check for ConfigValues 2022-09-01 21:29:07 +02:00
4167a222d7 ATOMIC_FS_UPDATE now Projecte-Define 2022-08-28 23:27:28 +02:00
f9498dac7d made FW-Version same Format as FS.Version 2022-08-28 23:27:01 +02:00
b9f658b111 FlashFS now gzip-Updatefiles & FS-Versioning imp. 2022-08-28 23:26:09 +02:00
4e34aaa24a Added Documentation 2022-08-24 23:13:27 +02:00
084925b844 missed include 2022-08-24 23:10:11 +02:00
e6a861185c Purging now also possible from WebUI 2022-08-24 23:09:26 +02:00
e12971b971 some debugging 2022-08-24 23:08:57 +02:00
4f5fdb7af4 corrected Tank-Lvel-Calculation 2022-08-24 23:08:29 +02:00
730f020f41 Updated DTC-Descriptions 2022-08-24 22:13:30 +02:00
3af1cfcb1b unimportant Serial Format Fix 2022-08-24 21:00:17 +02:00
685832cff8 Improved EEPROM-Formatting 2022-08-24 20:59:00 +02:00
3811834927 json now gets also gzipped 2022-08-24 20:58:12 +02:00
0025f8b0be DTC-Description now via Modal-Dialog 2022-08-24 16:49:40 +02:00
a30f56ff58 small fix 2022-08-24 07:30:51 +02:00
26942dd946 fixed missing name-tag in index.html 2022-08-24 07:27:53 +02:00
c997949c5b disabled Serial-debugging 2022-08-23 21:43:04 +02:00
1b0498ee5a fixed icons in manifest-File 2022-08-23 21:42:45 +02:00
82043ed9be re-added Firmware-Upload-Button after BS4-Update 2022-08-23 01:11:53 +02:00
f67817adb5 Format-Tweaks on WebUI 2022-08-23 00:50:52 +02:00
da19ebcc09 changed FlashVersion and incereased Buffer-Size 2022-08-23 00:48:00 +02:00
eaf2c9d8a8 DTC-Icon-fix 2022-08-23 00:12:03 +02:00
9072f2b3e2 added Google-Based Font locally 2022-08-23 00:11:44 +02:00
7d669dc04f updated .gitignore 2022-08-22 23:39:57 +02:00
79a7ca6fc1 renamed data-folder to data_src 2022-08-22 23:39:33 +02:00
821e94eec8 Updated WebUI to Bootstrap 4 2022-08-22 23:36:27 +02:00
0d9acaf43e preparing utility-script for gzip-littlefs 2022-08-22 23:32:51 +02:00
35361449eb some Fixes on Webui 2022-08-22 21:25:14 +02:00
45363b10fe missed ) on if-statement 2022-08-22 14:53:56 +02:00
c7f26bee32 made EEPROM-Format on WebUI more "secure" 2022-08-22 14:49:14 +02:00
984affb5a7 missed adjust of declaration for GetFlashVersion 2022-08-22 14:45:44 +02:00
d78c73d61f fixed missed define-change for WiFi-Clientmode 2022-08-22 14:29:40 +02:00
812a094e50 prevent Lube if Tank is empty (DryRun-Protection) 2022-08-22 14:29:01 +02:00
2ada3d9a61 EEPROM reset-Function added to WebUI 2022-08-22 14:28:32 +02:00
6ffe239cae Added DTC for LittleFS-Image Version mismatch 2022-08-22 14:26:37 +02:00
bd4c1d9d53 DTCs now hav severity and improved DTCMaintenance 2022-08-22 14:13:55 +02:00
808709f5c2 sanity-check now functional 2022-08-22 09:23:01 +02:00
080742c88c Math error ! -.- 2022-08-21 20:31:48 +02:00
38185e9056 improved DTC-Handling for EEPROM 2022-08-21 19:57:20 +02:00
17ed1ff7fa Added define to disable flashing of LED 2022-08-21 15:47:58 +02:00
0e34e7121f changed PurgePulse Vars to uint16_t 2022-08-21 15:47:22 +02:00
4ce550c668 start of sanitycheck-header 2022-08-21 11:28:58 +02:00
a6f5b4ef65 HTML5-compliance fix 2022-08-19 12:30:21 +02:00
7c38d02bf8 Fixed crash bc Stackoverflow from within Webserver 2022-08-19 11:25:26 +02:00
01ba4b7333 WebUI improvement to prevent empty fields 2022-08-19 08:34:37 +02:00
3048c6c2a1 changed restart behaviour after Firmware-Update 2022-08-19 08:16:33 +02:00
a6ae30d655 WebUI-improvements 2022-08-19 00:15:40 +02:00
01af8cba3c Improved Feature Control 2022-08-19 00:10:42 +02:00
4843cc15c9 Merge branches 'master' and 'master' of ssh://git.hiabuto.net:2222/souko/Kettenoeler 2022-08-14 17:38:49 +02:00
0b9ef67c39 multiple changes 2022-08-14 17:38:45 +02:00
94e34fb593 Bugfix in DTC and SystemStatus Handling 2022-05-27 13:33:44 +02:00
ad6332acd4 Restructured Folders 2022-05-07 23:24:42 +02:00
f514ee62fc Updated BOM-Excel for PCB Rev 1.4 2022-05-07 23:23:11 +02:00
b0ddece378 some Improvements with DTC and Init at Startup 2022-05-06 22:38:24 +02:00
d5508137d3 some Info avout FW-Version added to WebUI 2022-05-06 22:35:44 +02:00
27437555f8 Added Odometer and added EEPROM-End Detection 2022-05-05 21:07:24 +02:00
1c0ab060ff changed PreProcessor-Stuff for Feature-Handling 2022-05-04 23:06:15 +02:00
2b5039b8ab changed EEPROM-Stuff and added DTC if EE-Ver changed 2022-05-02 20:29:17 +02:00
c3e71d4759 Disabled WiFi-Client-Mode 2022-05-01 20:41:36 +02:00
3c0a7c3c59 Added BOM for PCB V 1.3 2022-05-01 20:41:16 +02:00
818d5843b3 Fixed Bug in DTC-Handling 2022-05-01 15:15:32 +02:00
872f577d35 more DTCs 2022-03-10 19:44:43 +01:00
cda2bb7afc removed some Serial.print 2022-03-09 23:05:17 +01:00
dab826b862 Added DTC-Table in WebUI and improved DTC-System 2022-03-09 20:25:02 +01:00
6e0b7581eb removed unnececary stuff 2022-03-08 23:03:10 +01:00
3fffc1f0b1 added Form for Lubrication-Setting on WebUI 2022-03-08 22:32:16 +01:00
c0a6069006 EEPROM-request now reset after Execution 2022-03-08 22:32:01 +01:00
a74f02fc61 huge improvement on WebUI with caching 2022-03-08 22:31:37 +01:00
606bf4e3ee env-update 2022-03-08 21:25:37 +01:00
e68a0b4d3e more progress on webUI 2022-03-08 21:23:52 +01:00
86e289f56f improved crc 2022-03-08 21:23:27 +01:00
7a84b80126 added dtcs for Signal-Timeout and empty tank 2022-03-08 21:23:06 +01:00
0bc6d01a5f more webui 2022-02-27 22:23:58 +01:00
6ba4c40166 more WebUI-Progress 2022-02-17 09:17:16 +01:00
fa23e72fbc Added HW-Serialport to Pinheader 2022-02-15 23:28:10 +01:00
7bd01a108a more Progress on WebUI 2022-02-15 23:27:53 +01:00
355b00a6cc fixed depencies 2022-02-15 23:27:39 +01:00
b361db164d type correction 2022-02-15 23:27:25 +01:00
16ec5d6e6d removed SoftwareSerial 2022-02-15 23:27:14 +01:00
d940d7aa78 More WebUI-Stuff 2022-02-15 22:37:58 +01:00
7167efd1b1 more Progress on WebUI 2022-02-15 20:58:32 +01:00
cc4a23f6df Starting to replace ESPUI-Lib due to OOM-Issues 2022-02-10 22:33:52 +01:00
00b28e5d5e Added first Code for GPS-Module Support 2022-02-10 22:32:40 +01:00
8b4e55d2dd moved some Strings to Flash 2022-02-10 22:31:11 +01:00
e6fa1e1ccd made RemoteDebug optional per define 2022-02-10 09:54:24 +01:00
efae15867a moved Tank-stuff to correct Tab in WebUI 2022-02-05 01:29:27 +01:00
7187d512e5 changed WebUI-Title 2022-02-05 00:31:16 +01:00
5824a32ad2 Code Formatting (Prettier) 2022-02-04 23:34:51 +01:00
055183ce90 PersistenceData gets stored cyclic now. 2022-02-04 22:26:26 +01:00
6ae9c273cb fixed warnings 2022-02-04 21:33:27 +01:00
d92818d4e5 CAN ist working 2022-02-04 21:28:49 +01:00
1ace7a8d6a Reworked method to calc traveled distance 2022-02-04 21:28:18 +01:00
39fc8af955 Reworked EEPROM-Handling by Flag 2022-02-04 21:24:15 +01:00
9571f5bbcc Titleblock in Schematic 2022-01-31 09:26:29 +01:00
b029243760 Added defines for PCB_revisions 2022-01-31 09:26:10 +01:00
152a324c9d Added Config-options fpr Different SpeedSources 2022-01-19 22:23:36 +01:00
f2e0e70647 fixed missing cases in systemStatus Text 2022-01-19 22:21:54 +01:00
8e6941b5bc fixed overflow of remaining tank 2022-01-19 22:21:12 +01:00
f3ce691b7d Added CAN-Bus Interface to PCB 2022-01-15 19:35:20 +01:00
aefbdc43bc Added Build-Define for WiFi-Client 2022-01-14 21:28:50 +01:00
08c00efbdf Added BOM Excel 2022-01-14 21:28:01 +01:00
cdbeb2b8c3 converted Schematic to KiCad V6.0 2022-01-14 15:39:59 +01:00
cfc6b144f3 Reworked Button-Stuff 2022-01-14 15:36:17 +01:00
e1ca503bd2 Adjusted Pulse/Pause to match Requirements of Pump 2022-01-13 22:00:31 +01:00
d68b562126 Some Debugs and Corrections for LED-Status 2022-01-13 21:59:53 +01:00
2217d68026 Added Button-Function Rain/Normal Switch 2022-01-13 21:59:32 +01:00
acb3c97c02 periodic Update of WebUI 2022-01-12 01:10:21 +01:00
70ea944dc3 made Bleeding/Purging stored to EEPROM 2022-01-12 00:53:22 +01:00
cff7c7b29c shorter Pulses for Pump 2022-01-12 00:52:59 +01:00
ea4895c262 fixed Warnings 2022-01-12 00:52:27 +01:00
85 changed files with 114273 additions and 3197 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
Bestellung.xlsx

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

1
Hardware/.gitignore vendored
View File

@@ -28,3 +28,4 @@ fp-info-cache
*.csv
gerber/
*-backups/

BIN
Hardware/BOM_Reichelt.xlsx Normal file

Binary file not shown.

74603
Hardware/Device.kicad_sym Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,443 @@
{
"board": {
"design_settings": {
"defaults": {
"board_outline_line_width": 0.049999999999999996,
"copper_line_width": 0.19999999999999998,
"copper_text_italic": false,
"copper_text_size_h": 1.5,
"copper_text_size_v": 1.5,
"copper_text_thickness": 0.3,
"copper_text_upright": false,
"courtyard_line_width": 0.049999999999999996,
"dimension_precision": 4,
"dimension_units": 3,
"dimensions": {
"arrow_length": 1270000,
"extension_offset": 500000,
"keep_text_aligned": true,
"suppress_zeroes": false,
"text_position": 0,
"units_format": 1
},
"fab_line_width": 0.09999999999999999,
"fab_text_italic": false,
"fab_text_size_h": 1.0,
"fab_text_size_v": 1.0,
"fab_text_thickness": 0.15,
"fab_text_upright": false,
"other_line_width": 0.09999999999999999,
"other_text_italic": false,
"other_text_size_h": 1.0,
"other_text_size_v": 1.0,
"other_text_thickness": 0.15,
"other_text_upright": false,
"pads": {
"drill": 0.762,
"height": 1.524,
"width": 1.524
},
"silk_line_width": 0.12,
"silk_text_italic": false,
"silk_text_size_h": 1.0,
"silk_text_size_v": 1.0,
"silk_text_thickness": 0.15,
"silk_text_upright": false,
"zones": {
"45_degree_only": false,
"min_clearance": 0.19999999999999998
}
},
"diff_pair_dimensions": [],
"drc_exclusions": [],
"meta": {
"filename": "board_design_settings.json",
"version": 2
},
"rule_severities": {
"annular_width": "error",
"clearance": "error",
"copper_edge_clearance": "error",
"courtyards_overlap": "error",
"diff_pair_gap_out_of_range": "error",
"diff_pair_uncoupled_length_too_long": "error",
"drill_out_of_range": "error",
"duplicate_footprints": "warning",
"extra_footprint": "warning",
"footprint_type_mismatch": "error",
"hole_clearance": "error",
"hole_near_hole": "error",
"invalid_outline": "error",
"item_on_disabled_layer": "error",
"items_not_allowed": "error",
"length_out_of_range": "error",
"malformed_courtyard": "error",
"microvia_drill_out_of_range": "error",
"missing_courtyard": "ignore",
"missing_footprint": "warning",
"net_conflict": "warning",
"npth_inside_courtyard": "ignore",
"padstack": "error",
"pth_inside_courtyard": "ignore",
"shorting_items": "error",
"silk_over_copper": "warning",
"silk_overlap": "warning",
"skew_out_of_range": "error",
"through_hole_pad_without_hole": "error",
"too_many_vias": "error",
"track_dangling": "warning",
"track_width": "error",
"tracks_crossing": "error",
"unconnected_items": "error",
"unresolved_variable": "error",
"via_dangling": "warning",
"zone_has_empty_net": "error",
"zones_intersect": "error"
},
"rule_severitieslegacy_courtyards_overlap": true,
"rule_severitieslegacy_no_courtyard_defined": false,
"rules": {
"allow_blind_buried_vias": false,
"allow_microvias": false,
"max_error": 0.005,
"min_clearance": 0.0,
"min_copper_edge_clearance": 0.049999999999999996,
"min_hole_clearance": 0.25,
"min_hole_to_hole": 0.25,
"min_microvia_diameter": 0.19999999999999998,
"min_microvia_drill": 0.09999999999999999,
"min_silk_clearance": 0.0,
"min_through_hole_diameter": 0.3,
"min_track_width": 0.19999999999999998,
"min_via_annular_width": 0.049999999999999996,
"min_via_diameter": 0.39999999999999997,
"use_height_for_length_calcs": true
},
"track_widths": [
0.0,
0.5,
1.0,
2.0
],
"via_dimensions": [
{
"diameter": 0.0,
"drill": 0.0
},
{
"diameter": 0.7,
"drill": 0.35
},
{
"diameter": 1.0,
"drill": 0.6
},
{
"diameter": 2.0,
"drill": 1.0
}
],
"zones_allow_external_fillets": false,
"zones_use_no_outline": true
},
"layer_presets": []
},
"boards": [],
"cvpcb": {
"equivalence_files": []
},
"erc": {
"erc_exclusions": [],
"meta": {
"version": 0
},
"pin_map": [
[
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
2,
0,
1,
0,
0,
1,
0,
2,
2,
2,
2
],
[
0,
0,
0,
0,
0,
0,
1,
0,
1,
0,
1,
2
],
[
0,
1,
0,
0,
0,
0,
1,
1,
2,
1,
1,
2
],
[
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
2
],
[
1,
1,
1,
1,
1,
0,
1,
1,
1,
1,
1,
2
],
[
0,
0,
0,
1,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
2,
1,
2,
0,
0,
1,
0,
2,
2,
2,
2
],
[
0,
2,
0,
1,
0,
0,
1,
0,
2,
0,
0,
2
],
[
0,
2,
1,
1,
0,
0,
1,
0,
2,
0,
0,
2
],
[
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2
]
],
"rule_severities": {
"bus_definition_conflict": "error",
"bus_entry_needed": "error",
"bus_label_syntax": "error",
"bus_to_bus_conflict": "error",
"bus_to_net_conflict": "error",
"different_unit_footprint": "error",
"different_unit_net": "error",
"duplicate_reference": "error",
"duplicate_sheet_names": "error",
"extra_units": "error",
"global_label_dangling": "warning",
"hier_label_mismatch": "error",
"label_dangling": "error",
"lib_symbol_issues": "warning",
"multiple_net_names": "warning",
"net_not_bus_member": "warning",
"no_connect_connected": "warning",
"no_connect_dangling": "warning",
"pin_not_connected": "error",
"pin_not_driven": "error",
"pin_to_pin": "warning",
"power_pin_not_driven": "error",
"similar_labels": "warning",
"unannotated": "error",
"unit_value_mismatch": "error",
"unresolved_variable": "error",
"wire_dangling": "error"
}
},
"libraries": {
"pinned_footprint_libs": [],
"pinned_symbol_libs": []
},
"meta": {
"filename": "oiler SMD.kicad_pro",
"version": 1
},
"net_settings": {
"classes": [
{
"bus_width": 12.0,
"clearance": 0.2,
"diff_pair_gap": 0.25,
"diff_pair_via_gap": 0.25,
"diff_pair_width": 0.2,
"line_style": 0,
"microvia_diameter": 0.3,
"microvia_drill": 0.1,
"name": "Default",
"pcb_color": "rgba(0, 0, 0, 0.000)",
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.25,
"via_diameter": 0.8,
"via_drill": 0.4,
"wire_width": 6.0
}
],
"meta": {
"version": 2
},
"net_colors": null
},
"pcbnew": {
"last_paths": {
"gencad": "",
"idf": "",
"netlist": "Rehoiler SMD.net",
"specctra_dsn": "",
"step": "",
"vrml": ""
},
"page_layout_descr_file": ""
},
"schematic": {
"annotate_start_num": 0,
"drawing": {
"default_line_thickness": 6.0,
"default_text_size": 50.0,
"field_names": [],
"intersheets_ref_own_page": false,
"intersheets_ref_prefix": "",
"intersheets_ref_short": false,
"intersheets_ref_show": false,
"intersheets_ref_suffix": "",
"junction_size_choice": 3,
"label_size_ratio": 0.25,
"pin_symbol_size": 25.0,
"text_offset_ratio": 0.08
},
"legacy_lib_dir": "",
"legacy_lib_list": [],
"meta": {
"version": 1
},
"net_format_name": "Pcbnew",
"ngspice": {
"fix_include_paths": true,
"fix_passive_vals": false,
"meta": {
"version": 0
},
"model_mode": 0,
"workbook_filename": ""
},
"page_layout_descr_file": "",
"plot_directory": "",
"spice_adjust_passive_values": false,
"spice_external_command": "spice \"%I\"",
"subpart_first_id": 65,
"subpart_id_separator": 0
},
"sheets": [
[
"b1ddb058-f7b2-429c-9489-f4e2242ad7e5",
""
]
],
"text_variables": {}
}

3585
Hardware/oiler SMD.kicad_sch Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,4 @@
data/
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json

View File

@@ -3,5 +3,8 @@
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

View File

@@ -1,33 +0,0 @@
#ifndef _COMMON_H_
#define _COMMON_H_
#define Q(x) #x
#define QUOTE(x) Q(x)
#define GPIO_BUTTON D5
#define GPIO_LED D6
#define GPIO_TRIGGER D4
#define GPIO_PUMP D3
#ifndef HOST_NAME
#define HOST_NAME "ChainLube_%06X" // Use printf-Formatting - Chip-ID (uin32_t) will be added
#endif
#ifndef OTA_DELAY
#define OTA_DELAY 50 // ticks -> 10ms / tick
#endif
#ifndef ADMIN_PASSWORD
#error "You need to define ADMIN_PASSWORD for OTA-Update"
#endif
#ifndef WIFI_PASSWORD
#error "You must define an WIFI_PASSWORD for OTA-Update"
#endif
#ifndef WIFI_SSID
#error "You must define an WIFI_SSID for OTA-Update"
#endif
#ifndef WIFI_AP_PASSWORD
#error "You must define an WIFI_AP_PASSWORD for Standalone AP-Mode"
#endif
#endif

View File

@@ -1,44 +0,0 @@
#include <Arduino.h>
#include "config.h"
LubeConfig_t LubeConfig;
void StoreConfig_EEPROM()
{
LubeConfig.checksum = 0;
LubeConfig.checksum = Checksum_EEPROM((uint8_t *)&LubeConfig, sizeof(LubeConfig));
EEPROM.begin(512);
EEPROM.put(0, LubeConfig);
EEPROM.commit();
EEPROM.end();
}
void GetConfig_EEPROM()
{
EEPROM.begin(512);
EEPROM.get(0, LubeConfig);
EEPROM.end();
uint32_t checksum = LubeConfig.checksum;
LubeConfig.checksum = 0;
if (Checksum_EEPROM((uint8_t *)&LubeConfig, sizeof(LubeConfig)) == checksum)
Serial.printf("Checksum OK");
else
Serial.printf("Checksum BAD");
}
uint32_t Checksum_EEPROM(uint8_t const *data, size_t len)
{
if (data == NULL)
return 0;
uint32_t crc = 0;
while (len--)
{
crc ^= *data++;
for (uint8_t k = 0; k < 8; k++)
crc = crc & 1 ? (crc >> 1) ^ 0xb2 : crc >> 1;
}
return crc ^ 0xff;
}

View File

@@ -1,29 +0,0 @@
#ifndef _CONFIG_H_
#define _CONFIG_H_
#include <Arduino.h>
#include <EEPROM.h>
typedef struct
{
uint32_t DistancePerLube_Default = 0;
uint32_t DistancePerLube_Rain = 0;
uint32_t tankCapacity_ml = 320;
uint32_t amountPerDose_µl = 0;
uint32_t tankRemain_µl = 0;
uint8_t TankRemindAtPercentage = 30;
uint8_t PulsePerRevolution = 1;
uint32_t TireWidth_mm = 150;
uint32_t TireWidthHeight_Ratio = 70;
uint32_t RimDiameter_Inch = 18;
uint32_t DistancePerRevolution_mm = 2000;
uint32_t checksum = 0;
} LubeConfig_t;
void StoreConfig_EEPROM();
void GetConfig_EEPROM();
uint32_t Checksum_EEPROM(uint8_t const *data, size_t len);
extern LubeConfig_t LubeConfig;
#endif // _CONFIG_H_

View File

@@ -1,25 +0,0 @@
#ifndef _GLOBALS_H_
#define _GLOBALS_H_
#include <Arduino.h>
typedef enum eSystem_Status
{
sysStat_NOP,
sysStat_Startup,
sysStat_Normal,
sysStat_Rain,
sysStat_Purge,
sysStat_Error
} tSystem_Status;
typedef struct Globals_t {
tSystem_Status systemStatus = sysStat_Startup;
uint8_t purgePulses= 0;
tSystem_Status resumeStatus = sysStat_Startup;
};
extern Globals_t globals;
extern uint32_t TravelDistance_highRes;
#endif

View File

@@ -1,65 +0,0 @@
#include "lubeapp.h"
extern void LED_Process(tSystem_Status newStatus = sysStat_NOP);
uint32_t lubePulseTimestamp = 0;
void RunLubeApp(volatile uint32_t *wheelPulseCounter)
{
uint32_t LubeDistance = 0;
// Calculate traveled Distance in mm
TravelDistance_highRes += (*wheelPulseCounter * (LubeConfig.DistancePerRevolution_mm / LubeConfig.PulsePerRevolution));
*wheelPulseCounter = 0;
// check if we have to Lube!
switch (globals.systemStatus)
{
case sysStat_Startup:
globals.systemStatus = sysStat_Normal;
globals.resumeStatus = sysStat_Normal;
break;
case sysStat_Normal:
if (TravelDistance_highRes / 1000 > LubeConfig.DistancePerLube_Default)
{
LubePulse();
TravelDistance_highRes = 0;
}
break;
case sysStat_Rain:
if (TravelDistance_highRes / 1000 > LubeConfig.DistancePerLube_Rain)
{
LubePulse();
TravelDistance_highRes = 0;
}
break;
case sysStat_Purge:
if (globals.purgePulses > 0)
{
if (lubePulseTimestamp + LUBE_PULSE_LENGHT_MS + LUBE_PULSE_PAUSE_MS < millis())
{
LubePulse();
globals.purgePulses--;
}
}
else
{
globals.systemStatus = globals.resumeStatus;
}
break;
}
// maintain Pin-State of Lube-Pump
if (lubePulseTimestamp > millis())
digitalWrite(GPIO_PUMP, HIGH);
else
digitalWrite(GPIO_PUMP, LOW);
}
void LubePulse()
{
lubePulseTimestamp = millis() + LUBE_PULSE_LENGHT_MS;
LubeConfig.tankRemain_µl = LubeConfig.tankRemain_µl - LubeConfig.amountPerDose_µl;
}

View File

@@ -1,342 +0,0 @@
#include <Arduino.h>
#include <Wire.h>
#include <U8g2lib.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266mDNS.h>
#include <ArduinoOTA.h>
#include <RemoteDebug.h>
#include <FastLED.h>
#include <Ticker.h>
#include "common.h"
#include "rmtdbghelp.h"
#include "lubeapp.h"
#include "webui.h"
#include "config.h"
#include "globals.h"
const char *ssid = QUOTE(WIFI_SSID);
const char *password = QUOTE(WIFI_PASSWORD);
const uint32_t connectTimeoutMs = 5000;
#ifdef DEBUG
const bool debug_flag = true;
#else
const bool debug_flag = false;
#endif
bool startSetupMode = false;
char DeviceName[33];
Globals_t globals;
uint32_t TravelDistance_highRes;
volatile uint32_t wheel_pulse = 0;
U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(-1);
RemoteDebug Debug;
ESP8266WiFiMulti wifiMulti;
CRGB leds[1];
// Function-Prototypes
String IpAddress2String(const IPAddress &ipAddress);
void processCmdRemoteDebug();
void RemotDebug_printSystemInfo();
void RemoteDebug_printWifiInfo();
void wifiMaintainConnectionTicker_callback();
void IRAM_ATTR trigger_ISR();
void LED_Process(tSystem_Status newStatus = sysStat_NOP);
void DisplayProcess();
Ticker WiFiMaintainConnectionTicker(wifiMaintainConnectionTicker_callback, 1000, 0, MILLIS);
void setup()
{
system_update_cpu_freq(SYS_CPU_80MHZ);
snprintf(DeviceName, 32, HOST_NAME, ESP.getChipId());
WiFi.mode(WIFI_OFF);
WiFi.persistent(false);
Serial.begin(115200);
Serial.setDebugOutput(true);
Serial.println("Souko's ChainLube Mk1");
Serial.println(DeviceName);
GetConfig_EEPROM();
u8x8.begin();
u8x8.setFont(u8x8_font_chroma48medium8_r);
FastLED.addLeds<WS2811, GPIO_LED, GRB>(leds, 1); // GRB ordering is assumed
pinMode(GPIO_TRIGGER, INPUT_PULLUP);
pinMode(GPIO_BUTTON, INPUT_PULLUP);
pinMode(GPIO_PUMP, OUTPUT);
attachInterrupt(digitalPinToInterrupt(GPIO_TRIGGER), trigger_ISR, FALLING);
WiFi.mode(WIFI_STA);
WiFi.setHostname(DeviceName);
wifiMulti.addAP(QUOTE(WIFI_SSID), QUOTE(WIFI_PASSWORD));
WiFiMaintainConnectionTicker.start();
if (MDNS.begin(DeviceName))
MDNS.addService("telnet", "tcp", 23);
Debug.begin(DeviceName); // Initialize the WiFi server
Debug.setResetCmdEnabled(true); // Enable the reset command
Debug.showProfiler(true); // Profiler (Good to measure times, to optimize codes)
Debug.showColors(true); // Colors
Debug.setPassword(QUOTE(ADMIN_PASSWORD));
Debug.setHelpProjectsCmds(helpCmd);
Debug.setCallBackProjectCmds(&processCmdRemoteDebug);
ArduinoOTA.setPort(8266);
ArduinoOTA.setHostname(DeviceName);
ArduinoOTA.setPassword(QUOTE(ADMIN_PASSWORD));
ArduinoOTA.onStart([]()
{
u8x8.clearDisplay();
u8x8.drawString(0, 0, "OTA-Update");
u8x8.refreshDisplay(); });
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total)
{
static bool refreshed = false;
if (!refreshed)
{
u8x8.clearDisplay();
refreshed = true;
u8x8.drawString(0, 0, "OTA Upload");
}
uint32_t percent = progress / (total / 100);
u8x8.setCursor(0, 1);
u8x8.printf("%d %%", percent);
u8x8.refreshDisplay(); });
ArduinoOTA.onEnd([]()
{
u8x8.clearDisplay();
u8x8.drawString(0, 0, "OTA-Restart");
u8x8.refreshDisplay(); });
ArduinoOTA.begin();
u8x8.clearDisplay();
u8x8.drawString(4, 0, "Souko's");
u8x8.drawString(1, 1, "ChainLube Mk1");
u8x8.refreshDisplay();
initWebUI();
}
void loop()
{
LED_Process(globals.systemStatus);
RunLubeApp(&wheel_pulse);
WiFiMaintainConnectionTicker.update();
DisplayProcess();
ArduinoOTA.handle();
Debug.handle();
yield();
}
String IpAddress2String(const IPAddress &ipAddress)
{
return String(ipAddress[0]) + String(".") +
String(ipAddress[1]) + String(".") +
String(ipAddress[2]) + String(".") +
String(ipAddress[3]);
}
void processCmdRemoteDebug()
{
String lastCmd = Debug.getLastCommand();
if (lastCmd == "sysinfo")
RemotDebug_printSystemInfo();
else if (lastCmd == "netinfo")
RemoteDebug_printWifiInfo();
}
void RemotDebug_printSystemInfo()
{
debugA("Souko's ChainOiler Mk1");
debugA("Hostname: %s", DeviceName);
FlashMode_t ideMode = ESP.getFlashChipMode();
debugA("Sdk version: %s", ESP.getSdkVersion());
debugA("Core Version: %s", ESP.getCoreVersion().c_str());
debugA("Boot Version: %u", ESP.getBootVersion());
debugA("Boot Mode: %u", ESP.getBootMode());
debugA("CPU Frequency: %u MHz", ESP.getCpuFreqMHz());
debugA("Reset reason: %s", ESP.getResetReason().c_str());
debugA("Flash Size: %d", ESP.getFlashChipRealSize());
debugA("Flash Size IDE: %d", ESP.getFlashChipSize());
debugA("Flash ide mode: %s", (ideMode == FM_QIO ? "QIO" : ideMode == FM_QOUT ? "QOUT"
: ideMode == FM_DIO ? "DIO"
: ideMode == FM_DOUT ? "DOUT"
: "UNKNOWN"));
debugA("OTA-Pass: %s", QUOTE(ADMIN_PASSWORD));
}
void RemoteDebug_printWifiInfo()
{
}
void wifiMaintainConnectionTicker_callback()
{
static uint32_t WiFiFailCount = 0;
const uint32_t WiFiFailMax = 20;
if (wifiMulti.run(connectTimeoutMs) == WL_CONNECTED)
{
return;
}
else
{
if (WiFiFailCount < WiFiFailMax)
{
WiFiFailCount++;
}
else
{
Serial.println("WiFi not connected! - Start AP");
WiFi.mode(WIFI_OFF);
WiFi.softAP(DeviceName, QUOTE(WIFI_AP_PASSWORD));
WiFiMaintainConnectionTicker.stop();
Serial.println("WiFi AP started, stopped Maintain-Timer");
}
}
}
void trigger_ISR()
{
wheel_pulse++;
}
void LED_Process(tSystem_Status newSysStatus)
{
typedef enum
{
LED_Startup,
LED_Normal,
LED_Confirm_Normal,
LED_Rain,
LED_Confirm_Rain,
LED_Purge,
LED_Error
} tLED_Status;
static tSystem_Status oldSysStatus = sysStat_NOP;
static tLED_Status LED_Status = LED_Startup;
uint8_t color = 0;
uint32_t timer = 0;
uint32_t timestamp = 0;
if (oldSysStatus != newSysStatus)
{
switch (newSysStatus)
{
case sysStat_Startup:
LED_Status = LED_Startup;
break;
case sysStat_Normal:
timestamp = millis() + 1500;
LED_Status = LED_Confirm_Normal;
break;
case sysStat_Rain:
timestamp = millis() + 1500;
LED_Status = LED_Confirm_Rain;
break;
case sysStat_Purge:
LED_Status = LED_Purge;
break;
case sysStat_Error:
LED_Status = LED_Error;
break;
}
oldSysStatus = newSysStatus;
}
timer = millis();
switch (LED_Status)
{
case LED_Startup:
FastLED.setBrightness(255);
timer = timer % 2000;
color = timer > 500 ? 0 : (uint8_t)(timer / 2);
leds[0] = CRGB(color, color, color);
break;
case LED_Confirm_Normal:
FastLED.setBrightness(255);
leds[0] = timer % 500 > 250 ? CRGB(0, 255, 0) : CRGB(0, 4, 0);
if (timestamp < timer)
{
LED_Status = LED_Normal;
FastLED.setBrightness(64);
}
break;
case LED_Normal:
leds[0] = timer % 2000 > 1950 ? CRGB(0, 255, 0) : CRGB(0, 4, 0);
break;
case LED_Confirm_Rain:
FastLED.setBrightness(255);
leds[0] = timer % 500 > 250 ? CRGB(0, 0, 255) : CRGB(0, 0, 4);
if (timestamp < timer)
{
LED_Status = LED_Rain;
FastLED.setBrightness(64);
}
break;
case LED_Rain:
leds[0] = timer % 2000 > 1950 ? CRGB(0, 0, 255) : CRGB(0, 0, 4);
break;
case LED_Purge:
timer = timer % 500;
color = timer / 2;
leds[0] = CRGB::DeepPink;
if (timer < 250)
FastLED.setBrightness(color);
else
FastLED.setBrightness(250 - color);
break;
case LED_Error:
leds[0] = timer % 500 > 250 ? CRGB::Red : CRGB::Black;
break;
default:
break;
}
FastLED.show();
}
void DisplayProcess()
{
u8x8.setCursor(0, 3);
uint32_t DistRemain = globals.systemStatus == sysStat_Normal ? LubeConfig.DistancePerLube_Default : LubeConfig.DistancePerLube_Rain;
DistRemain -= TravelDistance_highRes / 1000;
u8x8.printf("next Lube: %4dm\n", DistRemain);
u8x8.printf("Tank: %8dml\n", LubeConfig.tankRemain_µl / 1000);
u8x8.printf("Purges: %8d\n", globals.purgePulses);
u8x8.refreshDisplay();
}

View File

@@ -1,2 +0,0 @@
const char helpCmd[] = "sysinfo - System Info\r\n"
"netinfo - WiFi Info\r\n";

View File

@@ -1,131 +0,0 @@
#include "webui.h"
uint16_t tab_lube;
uint16_t tab_wheel;
uint16_t tab_tank;
uint16_t tab_maintenance;
uint16_t tab_store;
uint16_t num_lubedist_normal;
uint16_t num_lubedist_rain;
uint16_t button_lubedist;
uint16_t num_wheel_width;
uint16_t num_wheel_ratio;
uint16_t num_wheel_rim;
uint16_t button_wheelcalc;
uint16_t num_wheel_ppr;
uint16_t num_wheel_dist;
uint16_t num_tank_capacity;
uint16_t num_tank_notify;
uint16_t num_dose_per_pulse;
uint16_t label_tankRemain;
uint16_t button_reset_tank;
uint16_t num_purge_pulses;
uint16_t button_purge;
uint16_t button_store;
uint16_t button_reload;
uint16_t label_storeStatus;
void SettingChanged_Callback(Control *sender, int type)
{
if (sender->id == num_lubedist_normal)
LubeConfig.DistancePerLube_Default = ESPUI.getControl(num_lubedist_normal)->value.toInt();
else if (sender->id == num_lubedist_rain)
LubeConfig.DistancePerLube_Rain = ESPUI.getControl(num_lubedist_rain)->value.toInt();
else if (sender->id == num_wheel_width)
LubeConfig.TireWidth_mm = ESPUI.getControl(num_wheel_width)->value.toInt();
else if (sender->id == num_wheel_ratio)
LubeConfig.TireWidthHeight_Ratio = ESPUI.getControl(num_wheel_ratio)->value.toInt();
else if (sender->id == num_wheel_rim)
LubeConfig.RimDiameter_Inch = ESPUI.getControl(num_wheel_rim)->value.toInt();
else if (sender->id == num_wheel_ppr)
LubeConfig.PulsePerRevolution = ESPUI.getControl(num_wheel_ppr)->value.toInt();
else if (sender->id == num_wheel_dist)
LubeConfig.DistancePerRevolution_mm = ESPUI.getControl(num_wheel_dist)->value.toInt();
else if (sender->id == num_tank_capacity)
LubeConfig.tankCapacity_ml = ESPUI.getControl(num_tank_capacity)->value.toInt();
else if (sender->id == num_tank_notify)
LubeConfig.TankRemindAtPercentage = ESPUI.getControl(num_tank_notify)->value.toInt();
else if (sender->id == num_dose_per_pulse)
LubeConfig.amountPerDose_µl = ESPUI.getControl(num_dose_per_pulse)->value.toInt();
}
void buttons_Callback(Control *sender, int type)
{
if (type != B_UP)
return;
if (sender->id == button_wheelcalc)
{
LubeConfig.TireWidth_mm = ESPUI.getControl(num_wheel_width)->value.toInt();
LubeConfig.RimDiameter_Inch = ESPUI.getControl(num_wheel_rim)->value.toInt();
LubeConfig.TireWidthHeight_Ratio = ESPUI.getControl(num_wheel_ratio)->value.toInt();
LubeConfig.DistancePerRevolution_mm = (uint32_t)((((((float)LubeConfig.TireWidthHeight_Ratio / 100.0) * (float)LubeConfig.TireWidth_mm) * 2.0) + ((float)LubeConfig.RimDiameter_Inch * 25.4)) * 3.1416);
ESPUI.updateControlValue(num_wheel_dist, String(LubeConfig.DistancePerRevolution_mm));
}
if (sender->id == button_reset_tank)
{
LubeConfig.tankRemain_µl = LubeConfig.tankCapacity_ml * 1000;
ESPUI.print(label_tankRemain, String(LubeConfig.tankRemain_µl / 1000));
}
if (sender->id == button_purge)
{
int pulses = ESPUI.getControl(num_purge_pulses)->value.toInt();
Serial.printf("Starting to Purge with %d pulses", pulses);
globals.purgePulses = pulses;
globals.resumeStatus = globals.systemStatus;
globals.systemStatus = sysStat_Purge;
}
if (sender->id == button_store)
{
StoreConfig_EEPROM();
ESPUI.print(label_storeStatus, "Successfully Stored Settings");
}
if (sender->id == button_reload)
{
GetConfig_EEPROM();
ESPUI.print(label_storeStatus, "Successfully Reloaded Settings");
}
}
void initWebUI()
{
tab_lube = ESPUI.addControl(ControlType::Tab, "Dosierung", "Dosierung");
tab_wheel = ESPUI.addControl(ControlType::Tab, "Erfassung", "Erfassung");
tab_tank = ESPUI.addControl(ControlType::Tab, "Tank", "Tank");
tab_maintenance = ESPUI.addControl(ControlType::Tab, "Wartung", "Wartung");
tab_store = ESPUI.addControl(ControlType::Tab, "Speichern", "Speichern");
num_lubedist_normal = ESPUI.addControl(ControlType::Number, "Öl-Impuls alle x Meter (Normal)", String(LubeConfig.DistancePerLube_Default), ControlColor::Emerald, tab_lube, &SettingChanged_Callback);
num_lubedist_rain = ESPUI.addControl(ControlType::Number, "Öl-Impuls alle x Meter (Regen)", String(LubeConfig.DistancePerLube_Rain), ControlColor::Emerald, tab_lube, &SettingChanged_Callback);
num_wheel_width = ESPUI.addControl(ControlType::Number, "Reifenbreite (mm)", String(LubeConfig.TireWidth_mm), ControlColor::Peterriver, tab_wheel, &SettingChanged_Callback);
num_wheel_ratio = ESPUI.addControl(ControlType::Number, "Höhe/Breite-Verhältniss", String(LubeConfig.TireWidthHeight_Ratio), ControlColor::Peterriver, tab_wheel, &SettingChanged_Callback);
num_wheel_rim = ESPUI.addControl(ControlType::Number, "Felgendurchmesser (Zoll)", String(LubeConfig.RimDiameter_Inch), ControlColor::Peterriver, tab_wheel, &SettingChanged_Callback);
button_wheelcalc = ESPUI.addControl(ControlType::Button, "Abrollumfang aus Reifendaten berechnen", "Berechnen", ControlColor::Peterriver, tab_wheel, &buttons_Callback);
num_wheel_dist = ESPUI.addControl(ControlType::Number, "Wegstrecke pro Radumdrehung (mm)", String(LubeConfig.DistancePerRevolution_mm), ControlColor::Wetasphalt, tab_wheel, &SettingChanged_Callback);
num_wheel_ppr = ESPUI.addControl(ControlType::Number, "Sensorimpulse pro Umdrehung", String(LubeConfig.PulsePerRevolution), ControlColor::Wetasphalt, tab_wheel, &SettingChanged_Callback);
num_tank_capacity = ESPUI.addControl(ControlType::Number, "Tankinhalt maximal (ml)", String(LubeConfig.tankCapacity_ml), ControlColor::Carrot, tab_tank, &SettingChanged_Callback);
num_tank_notify = ESPUI.addControl(ControlType::Number, "Tankinhalt Warnung (%)", String(LubeConfig.TankRemindAtPercentage), ControlColor::Carrot, tab_tank, &SettingChanged_Callback);
num_dose_per_pulse = ESPUI.addControl(ControlType::Number, "Menge pro Pumpenimpuls (µl)", String(LubeConfig.amountPerDose_µl), ControlColor::Carrot, tab_tank, &SettingChanged_Callback);
label_tankRemain = ESPUI.addControl(ControlType::Label, "Tankinhalt verbleibend (ml, geschätzt)", String(LubeConfig.tankRemain_µl / 1000), ControlColor::Alizarin, tab_maintenance);
button_reset_tank = ESPUI.addControl(ControlType::Button, "Tankinhalt zurücksetzen", "Reset", ControlColor::Alizarin, tab_maintenance, &buttons_Callback);
num_purge_pulses = ESPUI.addControl(ControlType::Number, "Entlüftung Impulse", "10", ControlColor::Alizarin, tab_maintenance);
button_purge = ESPUI.addControl(ControlType::Button, "Leitung Entlüften", "Start", ControlColor::Alizarin, tab_maintenance, &buttons_Callback);
button_store = ESPUI.addControl(ControlType::Button, "Einstellungen permanent speichern", "Speichern", ControlColor::Turquoise, tab_store, &buttons_Callback);
button_reload = ESPUI.addControl(ControlType::Button, "Einstellungen neu laden", "Laden", ControlColor::Turquoise, tab_store, &buttons_Callback);
label_storeStatus = ESPUI.addControl(ControlType::Label, "Status", "", ControlColor::Turquoise, tab_store);
ESPUI.begin("Souko's ChainLube Mk1");
}

View File

@@ -1,12 +0,0 @@
#ifndef _WEBUI_H_
#define _WEBUI_H_
#include <Arduino.h>
#include <ESPUI.h>
#include "config.h"
#include "globals.h"
void initWebUI();
#endif

77
Software/assets/Logo.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 KiB

696
Software/data_src/index.htm Normal file
View File

@@ -0,0 +1,696 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>KTM CAN Chain Oiler</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="static/css/bootstrap.min.css">
<link rel="stylesheet" href="static/css/custom.css">
<link rel="stylesheet" href="static/css/tweaks.css">
<script src="static/js/jquery.min.js"></script>
<script src="static/js/bootstrap.min.js"></script>
<script src="static/js/websocket.js"></script>
<link rel="apple-touch-icon" sizes="180x180" href="static/img/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="static/img/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="static/img/favicon-16x16.png">
<link rel="manifest" href="static/img/site.webmanifest">
</head>
<body>
<nav class="navbar fixed-top navbar-dark bg-primary" id="navbar1">
<a class="navbar-brand" href="#">
<img src="static/img/logo.png" width="30" height="30" class="d-inline-block align-top mr-1" alt="">
KTM CAN ChainLube
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#collapsingNavbar"
aria-controls="collapsingNavbar" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="collapsingNavbar">
<ul class="navbar-nav nav mr-auto mt-2 mt-lg-0">
<li class="nav-item"><a class="nav-link active" role="tab" data-toggle="tab" href="#tab_home">Home</a></li>
<li class="nav-item"><a class="nav-link" role="tab" data-toggle="tab" href="#tab_source">Wegstrecke</a></li>
<li class="nav-item"><a class="nav-link" role="tab" data-toggle="tab" href="#tab_lube">Schmierung</a></li>
<li class="nav-item"><a class="nav-link" role="tab" data-toggle="tab" href="#tab_maintenance">Wartung</a></li>
<li class="nav-item"><a class="nav-link" role="tab" data-toggle="tab" href="#tab_sysinfo">Systeminfo</a></li>
<li class="nav-item"><a class="nav-link" role="tab" data-toggle="tab" href="#tab_fwupdate">Update</a></li>
</ul>
</div>
</nav>
<main class="container">
<!-- Tabs Content -->
<div class="tab-content">
<!-- Div Tab Home-->
<div id="tab_home" class="tab-pane fade show active" role="tabpanel">
<div class="col text-center">
<div class="jumbotron">
<img src="static/img/logo.png" width="120" height="120" class="img-fluid" alt="">
<h3 class="pt-3">KTM CAN Chain Lube</h3>
</div>
</div>
<hr />
<p>
<h4>Tankinhalt verbleibend</h4>
<div class="progress">
<div class="progress-bar text-light" role="progressbar" aria-valuenow="%TANK_REMAIN_CAPACITY%"
aria-valuemin="0" aria-valuemax="100" style="width: %TANK_REMAIN_CAPACITY%&#37;">
%TANK_REMAIN_CAPACITY%&#37;
</div>
</div>
</p>
<hr />
<p>
<h4>aktueller Modus</h4>
<input class="form-control" type="text" placeholder="%SYSTEM_STATUS%" readonly>
</p>
<hr />
<div %SHOW_DTC_TABLE%>
<p>
<h4>Fehlercodes</h4>
<table class="table">
<tbody>
<tr>
<th class="col-6" scope="col">Zeitstempel</th>
<th class="col-2" scope="col">Fehlercode</th>
<th class="col-2" scope="col">Schwere</th>
<th class="col-2" scope="col">Aktiv</th>
</tr>
%DTC_TABLE%
</tbody>
</table>
</p>
<hr />
</div>
</div>
<!-- Div Tab Home-->
<!-- Div Tab Source Settings-->
<div id="tab_source" class="tab-pane fade" role="tabpanel">
<h3>Wegstreckenerfassung</h3>
<hr />
<p>
<h4>Signalquelle</h4>
<form action="post.htm" method="POST" class="form-horizontal">
<div class="form-group row">
<label for="sourceselect" class="control-label col-4">Schnittstelle</label>
<div class="col-8">
<select id="sourceselect" name="sourceselect" class="select form-control">
%SOURCE_SELECT_OPTIONS%
</select>
</div>
</div>
<div class="alert alert-primary alert-dismissable show fade" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<strong>Achtung!</strong><br>
Bei Änderung der Signalquelle wird der CAN-Oiler neu gestartet.
Dadurch wird die WiFi-Verbindung getrennt und muss neu aufgebaut werden.
</div>
<div class="form-group row">
<div class="col text-center">
<button name="sourcesave" type="submit" class="btn btn-outline-primary">&Uuml;bernehmen</button>
</div>
</div>
</form>
</p>
<!-- Div Source:Impulse Settings-->
<div %SHOW_IMPULSE_SETTINGS%>
<hr />
<p>
<h4>Einstellungen Impulseingang</h4>
<form action="post.htm" method="POST" class="form-horizontal">
<div class="form-group row">
<label for="tirewidth" class="control-label col-4">Reifenbreite</label>
<div class="col-8">
<div class="input-group">
<input id="tirewidth" name="tirewidth" type="text" required="required" class="form-control"
value="%TIRE_WIDTH_MM%">
<div class="input-group-append">
<span class="input-group-text">mm</span>
</div>
</div>
</div>
</div>
<div class="form-group row">
<label for="tireratio" class="control-label col-4">Höhe/Breite-Verhältniss</label>
<div class="col-8">
<div class="input-group">
<input id="tireratio" name="tireratio" type="text" required="required" class="form-control"
value="%TIRE_RATIO%">
<div class="input-group-append">
<span class="input-group-text">mm</span>
</div>
</div>
</div>
</div>
<div class="form-group row">
<label for="tiredia" class="control-label col-4">Felgendurchmesser</label>
<div class="col-8">
<div class="input-group">
<input id="tiredia" name="tiredia" type="text" required="required" class="form-control"
value="%RIM_DIAMETER%">
<div class="input-group-append">
<span class="input-group-text">"</span>
</div>
</div>
</div>
</div>
<div class="form-group row">
<label for="pulserev" class="control-label col-4">Pulse pro Umdrehung</label>
<div class="col-8">
<div class="input-group">
<input id="pulserev" name="pulserev" type="text" required="required" class="form-control"
value="%PULSE_PER_REV%">
<div class="input-group-addon"></div>
</div>
</div>
</div>
<div class="form-group row">
<div class="col text-center">
<button name="pulsesave" type="submit" class="btn btn-outline-primary">Speichern</button>
</div>
</div>
</form>
</p>
<hr />
</div>
<!-- Div Source:Impulse Settings-->
<!-- Div Source:CAN Settings-->
<div %SHOW_CAN_SETTINGS%>
<hr />
<p>
<h4>Einstellungen CAN-Bus</h4>
<form action="post.htm" method="POST" class="form-horizontal">
<div class="form-group row">
<label for="cansource" class="control-label col-4">Model</label>
<div class="col-8">
<select id="cansource" name="cansource" class="select form-control">
%CANSOURCE_SELECT_OPTIONS%
</select>
</div>
</div>
<div class="form-group row">
<div class="col text-center">
<button name="cansave" type="submit" class="btn btn-outline-primary">Speichern</button>
</div>
</div>
</form>
</p>
<hr />
</div>
<!-- Div Source:CAN Settings-->
<!-- Div Source:GPS Settings-->
<div %SHOW_GPS_SETTINGS%>
<hr />
<p>
<h4>Einstellungen GPS</h4>
<form action="post.htm" method="POST" class="form-horizontal">
<div class="form-group row">
<label for="gpsbaud" class="control-label col-4">Baudrate</label>
<div class="col-8">
<select id="gpsbaud" name="gpsbaud" class="select form-control">
%GPSBAUD_SELECT_OPTIONS%
</select>
</div>
</div>
<div class="form-group row">
<div class="col text-center">
<button name="gpssave" type="submit" class="btn btn-outline-primary">Speichern</button>
</div>
</div>
</form>
</p>
<hr />
</div>
<!-- Div Source:GPS Settings-->
</div>
<!-- Div Tab Source Settings-->
<!-- Div Tab Lube -->
<div id="tab_lube" class="tab-pane fade" role="tabpanel">
<h3>Schmierung</h3>
<hr />
<p>
<h4>Dosierung</h4>
<form action="post.htm" method="POST" class="form-horizontal">
<div class="form-group row">
<label for="lubedistancenormal" class="control-label col-4">Normal (gr&uuml;n)</label>
<div class="col-8">
<div class="input-group">
<input id="lubedistancenormal" name="lubedistancenormal" value="%LUBE_DISTANCE_NORMAL%" type="text"
class="form-control" required="required">
<div class="input-group-append">
<span class="input-group-text">m</span>
</div>
</div>
</div>
</div>
<div class="form-group row">
<label for="lubedistancerain" class="control-label col-4">Regen (blau)</label>
<div class="col-8">
<div class="input-group">
<input id="lubedistancerain" name="lubedistancerain" value="%LUBE_DISTANCE_RAIN%" type="text"
class="form-control" required="required">
<div class="input-group-append">
<span class="input-group-text">m</span>
</div>
</div>
</div>
</div>
<div class="form-group row">
<div class="col text-center">
<button name="oilsave" type="submit" class="btn btn-outline-primary">Speichern</button>
</div>
</div>
</form>
</p>
<hr />
</div>
<!-- Div Tab Lube -->
<!-- Div Tab Maintenance -->
<div id="tab_maintenance" class="tab-pane fade" role="tabpanel">
<h3>Wartung</h3>
<hr />
<p>
<h4>&Ouml;lvorrat</h4>
<form action="post.htm" method="POST" class="form-horizontal">
<div class="form-group row">
<label for="tankcap" class="control-label col-4">Tankkapazität</label>
<div class="col-8">
<div class="input-group">
<input id="tankcap" name="tankcap" value="%TANK_CAPACITY%" type="text" class="form-control"
required="required">
<div class="input-group-append">
<span class="input-group-text">ml</span>
</div>
</div>
</div>
</div>
<div class="form-group row">
<label for="tankwarn" class="control-label col-4">Leer-Warnung</label>
<div class="col-8">
<div class="input-group">
<input id="tankwarn" name="tankwarn" value="%TANK_REMIND%" type="text" class="form-control"
required="required">
<div class="input-group-append">
<span class="input-group-text">&#37;</span>
</div>
</div>
</div>
</div>
<div class="form-group row">
<label for="pumppulse" class="control-label col-4">Menge pro Puls</label>
<div class="col-8">
<div class="input-group">
<input id="pumppulse" name="pumppulse" value="%AMOUNT_PER_DOSE%" type="text" class="form-control"
required="required">
<div class="input-group-append">
<span class="input-group-text">µl</span>
</div>
</div>
</div>
</div>
<div class="form-group row">
<label for="tankremain_maint" class="control-label col-4">Tankinhalt verbleibend</label>
<div class="col-8">
<div class="progress">
<div id="tankremain_maint" class="progress-bar text-light" role="progressbar"
aria-valuenow="%TANK_REMAIN_CAPACITY%" aria-valuemin="0" aria-valuemax="100"
style="width: %TANK_REMAIN_CAPACITY%&#37;">
%TANK_REMAIN_CAPACITY%&#37;
</div>
</div>
</div>
</div>
<div class="form-group row">
<div class="col text-center">
<button name="oilsave" type="submit" class="btn btn-outline-primary">Speichern</button>
<button name="resettank" type="submit" class="btn btn-outline-primary ml-2">Tank zurücksetzen</button>
</div>
</div>
</form>
</p>
<hr />
<p>
<h4>Entl&uuml;ftung</h4>
<form action="post.htm" method="POST" class="form-horizontal">
<div class="form-group row">
<label for="purgepulse" class="control-label col-4">Entl&uuml;ftung Dosierung</label>
<div class="col-8">
<div class="input-group">
<input id="purgepulse" name="purgepulse" value="%BLEEDING_PULSES%" type="text" class="form-control">
<div class="input-group-append">
<span class="input-group-text">Pulse</span>
</div>
</div>
</div>
</div>
<div class="form-group row">
<div class="col text-center">
<button name="maintsave" type="submit" class="btn btn-outline-primary">Speichern</button>
<button name="purgenow" type="submit" class="btn btn-outline-primary ml-2">Entlüftung starten</button>
</div>
</div>
</form>
</p>
<hr />
<p>
<h4>EEPROM formatieren</h4>
<div class="alert alert-primary alert-dismissable show fade" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<strong>Achtung!</strong><br>
Das Formatieren der EEPROM-Bereiche sollte nur ausgeführt werden wenn es unbedingt erforderlich ist!
Hierdurch werden alle Einstellungen zurück gesetzt bzw. alle Betriebsdaten gehen verloren.
Folgende Situationen erfordern unter anderem eine Formatierung:
- Erstinitialisierung (bei neu aufgebautem Gerät)
- Firmware-Update (nur wenn es die Release-Notes fordern)
</div>
<form action="post.htm" method="POST" class="form-horizontal">
<div class="form-group row">
<div class="offset-4 col-8">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="reset_ee_cfg" id="reset_ee_cfg">
<label class="form-check-label" for="reset_ee_cfg">
Bereich "CFG"
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="reset_ee_pds" id="reset_ee_pds">
<label class="form-check-label" for="reset_ee_pds">
Bereich "PDS"
</label>
</div>
</div>
</div>
<div class="form-group row">
<div class="col text-center">
<button name="reset_ee_btn" type="submit" class="btn btn-outline-primary">EEPROM formatieren</button>
</div>
</div>
</form>
</p>
<hr />
<p>
<h4>Ger&auml;t neustarten</h4>
<form action="post.htm" method="POST" class="form-horizontal">
<div class="form-group row">
<div class="col text-center">
<button name="reboot" type="submit" class="btn btn-outline-primary">Reboot</button>
</div>
</div>
</form>
</p>
<hr />
</div>
<!-- Div Tab Maintenance -->
<!-- Div Tab SystemInfo -->
<div id="tab_sysinfo" class="tab-pane fade" role="tabpanel">
<h3>Systeminfo</h3>
<hr />
<p>
<h4>Einstellungen</h4>
<table class="table">
<tbody>
<tr>
<th class="col-7" scope="col">Parameter</td>
<th class="col-5" scope="col">Value</td>
</tr>
<tr>
<td>DistancePerLube_Default</td>
<td>%LUBE_DISTANCE_NORMAL%</td>
</tr>
<tr>
<td>DistancePerLube_Rain</td>
<td>%LUBE_DISTANCE_RAIN%</td>
</tr>
<tr>
<td>tankCapacity_ml</td>
<td>%TANK_CAPACITY%</td>
</tr>
<tr>
<td>amountPerDose_&micro;l</td>
<td>%AMOUNT_PER_DOSE%</td>
</tr>
<tr>
<td>TankRemindAtPercentage</td>
<td>%TANK_REMIND%</td>
</tr>
<tr>
<td>PulsePerRevolution</td>
<td>%PULSE_PER_REV%</td>
</tr>
<tr>
<td>TireWidth_mm</td>
<td>%TIRE_WIDTH_MM%</td>
</tr>
<tr>
<td>TireWidthHeight_Ratio</td>
<td>%TIRE_RATIO%</td>
</tr>
<tr>
<td>RimDiameter_Inch</td>
<td>%RIM_DIAMETER%</td>
</tr>
<tr>
<td>DistancePerRevolution_mm</td>
<td>%DISTANCE_PER_REV%</td>
</tr>
<tr>
<td>BleedingPulses</td>
<td>%BLEEDING_PULSES%</td>
</tr>
<tr>
<td>SpeedSource</td>
<td>%SPEED_SOURCE%</td>
</tr>
<tr>
<td>GPSBaudRate</td>
<td>%GPS_BAUD%</td>
</tr>
<tr>
<td>CANSource</td>
<td>%CAN_SOURCE%</td>
</tr>
<tr>
<td>Checksum</td>
<td>%CONFIG_CHECKSUM%</td>
</tr>
</tbody>
</table>
</p>
<hr />
<p>
<h4>Betriebsdaten</h4>
<table class="table">
<tbody>
<tr>
<th class="col-7" scope="col">Parameter</td>
<th class="col-5" scope="col">Value</td>
</tr>
<tr>
<td>writeCycleCounter</td>
<td>%WRITE_CYCLE_COUNT%</td>
</tr>
<tr>
<td>PersistenceMarker</td>
<td>%PERSISTENCE_MARKER%</td>
</tr>
<tr>
<td>tankRemain_µl</td>
<td>%TANK_REMAIN_UL%</td>
</tr>
<tr>
<td>TravelDistance_highRes</td>
<td>%TRAVEL_DISTANCE_HIGHRES%</td>
</tr>
<tr>
<td>Odometer</td>
<td>%ODOMETER%,%ODOMETER_M%</td>
</tr>
<tr>
<td>checksum</td>
<td>%PERSISTANCE_CHECKSUM%</td>
</tr>
</table>
</p>
<hr />
<p>
<h4>Live Debug</h4>
<div class="form-group row">
<textarea class="form-control" spellcheck="false" id="livedebug-out" rows="3" readonly></textarea>
</div>
<div class="form-group row">
<div class="col text-center">
<button id="btn-ws-start" class="btn btn-outline-primary">Start</button>
<button id="btn-ws-stop" class="btn btn-outline-primary ml-2">Stop</button>
</div>
</div>
</p>
<hr />
</div>
<!-- Div Tab SystemInfo -->
<!-- Div Tab Firmware Update-->
<div id="tab_fwupdate" class="tab-pane fade" role="tabpanel">
<h3>Firmware</h3>
<hr />
<p>
<h4>Version-Info</h4>
<table class="table">
<tbody>
<tr>
<th class="col-7" scope="col">Parameter</td>
<th class="col-5" scope="col">Value</td>
</tr>
<tr>
<td>Firmware Version</td>
<td>%SW_VERSION%</td>
</tr>
<tr>
<td>Flash Version</td>
<td>%FS_VERSION%</td>
</tr>
</table>
</p>
<hr />
<p>
<h4>EEPROM-Backup</h4>
<div class="form-group row">
<div class="col text-center">
<a class="btn btn-outline-primary" href="eejson" role="button" id="ee-backup-download">Download</a>
</div>
</div>
</p>
<hr />
<p>
<h4>EEPROM-Restore</h4>
<form method='POST' action='eeRestore' enctype='multipart/form-data'>
<div class="form-group row">
<div class="custom-file">
<input type="file" name="ee-restore-file" class="custom-file-input" id="ee-restore-file" accept=".ee.json"
required />
<label class="custom-file-label" for="ee-restore-file">EEPROM-Backup ausw&auml;hlen</label>
</div>
</div>
<div class="form-group row">
<div class="col text-center">
<button name="submit" type="submit" class="btn btn-outline-primary">Restore starten</button>
</div>
</div>
</form>
</p>
<hr />
<p>
<h4>Firmware-Update</h4>
<form method='POST' action='doUpdate' enctype='multipart/form-data'>
<div class="form-group row">
<div class="custom-file">
<input type="file" name="fw-update-file" class="custom-file-input" id="fw-update-file"
accept=".fw.bin,.fs.gz" required />
<label class="custom-file-label" for="fw-update-file">Firmware-Update ausw&auml;hlen</label>
</div>
</div>
<div class="form-group row">
<div class="col text-center">
<button name="submit" type="submit" class="btn btn-outline-primary">Update starten</button>
</div>
</div>
</form>
</p>
<hr />
</div>
<!-- Div Tab Firmware Update-->
</div>
<!-- Tabs Content -->
</main>
<!-- Footer -->
<footer class="page-footer navbar-dark bg-primary font-small fixed-bottom">
<div class="container-fluid text-center">
<div class="footer-copyright text-center py-3">
<span class="text-muted">© 2022 -
<a class="text-reset fw-bold" href="https://eventronics.de/">Marcel Peterkau</a></span>
</div>
</div>
</footer>
<!-- Footer -->
<!-- Modal Dialog -->
<div class="modal fade" id="dtcModal" tabindex="-1" role="dialog" aria-labelledby="dtcModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="dtcModalLabel">DTC-Description</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p class="dtc-desc">DTC Description</p>
<p class="dtc-debugval">DTC DebugVal</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<!-- Modal Dialog -->
<script>
$('.navbar-nav>li>a').on('click', function () {
$('.navbar-collapse').collapse('hide');
});
document.querySelector('.custom-file-input').addEventListener('change', function (e) {
var fileName = document.getElementById("fw-update-file").files[0].name;
var nextSibling = e.target.nextElementSibling
nextSibling.innerText = fileName
});
$(document).ready(function () {
$("tr[data-dtc]").each(function (i) {
$(this).attr('data-toggle', "modal");
$(this).attr('data-target', "#dtcModal");
});
});
$('#dtcModal').on('show.bs.modal', function (event) {
var dtctr = $(event.relatedTarget)
var dtc = dtctr.data('dtc')
var debugval = dtctr.data('debugval')
var modal = $(this)
$.getJSON('static/tt_dtc/dtc_' + dtc + '.json', function (data) {
modal.find('.modal-title').text(data.title)
modal.find('.dtc-desc').text(data.description)
if (debugval > 0) {
modal.find('.dtc-debugval').text("Debugvalue: " + debugval)
}
else {
modal.find('.dtc-debugval').remove()
}
}).fail(function () {
console.log("An error has occurred.");
modal.find('.modal-title').text("Fehler")
modal.find('.dtc-desc').text("DTC-Beschreibung konnte nicht geladen werden")
});
});
</script>
</body>
</html>

View File

@@ -0,0 +1,28 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>KTM CAN Chain Oiler</title>
<meta http-equiv="content-type" content="text/html;charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="static/css/bootstrap.min.css">
<link rel="stylesheet" href="static/css/custom.css">
<script src="static/js/jquery.min.js"></script>
<script src="static/js/bootstrap.min.js"></script>
<link rel="apple-touch-icon" sizes="180x180" href="static/img/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="static/img/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="static/img/favicon-16x16.png">
<link rel="manifest" href="static/img/site.webmanifest">
<meta http-equiv="refresh" content="3; url='index.htm'" />
</head>
<body>
<div class="container" style="display: flex; justify-content: center; align-items: center; height: 100vh">
<div class="alert alert-success">
<strong>Bitte warten!</strong> Änderungen werden übernommen.
</div>
</div>
</body>
</html>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
@font-face {
font-family: 'Comfortaa';
font-style: normal;
font-weight: 300;
src: url(../fonts/comfortaa.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
body {
padding-top: 70px;
margin-bottom: 70px;
}
hr {
height: 2px;
border-width: 0;
color: gray;
background-color: gray
}
.dtc-debugval {
color: #F2771A;
font: 0.8rem Inconsolata, monospace;
background-color: black;
padding: 10px;
}

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 545 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@@ -0,0 +1 @@
{"name":"","short_name":"","icons":[{"src":"/static/img/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/static/img/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,68 @@
var gateway = `ws://${window.location.hostname}/ws`;
var websocket;
window.addEventListener("load", onLoad);
function initWebSocket() {
console.log("Trying to open a WebSocket connection...");
websocket = new WebSocket(gateway);
websocket.onopen = onOpen;
websocket.onclose = onClose;
websocket.onmessage = onMessage; // <-- add this line
}
function initButtons() {
document
.getElementById("btn-ws-stop")
.addEventListener("click", livedebug_stop);
document
.getElementById("btn-ws-start")
.addEventListener("click", livedebug_start);
}
function onOpen(event) {
console.log("Connection opened");
}
function onClose(event) {
console.log("Connection closed");
setTimeout(initWebSocket, 2000);
}
function onMessage(event) {
var livedebug_out = document.getElementById("livedebug-out");
var textarea_heigth = livedebug_out.scrollHeight;
livedebug_out.value += event.data;
livedebug_out.scrollTop = livedebug_out.scrollHeight;
do_resize(livedebug_out);
}
function onLoad(event) {
initWebSocket();
initButtons();
}
function livedebug_start() {
websocket.send("start");
}
function livedebug_stop() {
websocket.send("stop");
}
function do_resize(textbox) {
var maxrows = 15;
var minrows = 3;
var txt = textbox.value;
var cols = textbox.cols;
var arraytxt = txt.split("\n");
var rows = arraytxt.length;
for (i = 0; i < arraytxt.length; i++)
rows += parseInt(arraytxt[i].length / cols);
if (rows > maxrows) textbox.rows = maxrows;
else if (rows < minrows) textbox.rows = minrows;
else textbox.rows = rows;
}

View File

@@ -0,0 +1,4 @@
{
"title": "Ölvorrat leer",
"description": "Ölvorrat ist komplett leer. Den Ölvorrat auffüllen und im Menu 'Wartung' zurück setzen"
}

View File

@@ -0,0 +1,4 @@
{
"title": "Keine GPS-Verbindung",
"description": "Es wurde kein GPS-Signal über die serielle Schnittstelle empfangen, Prüfen sie die Verbindung und das GPS-Modul"
}

View File

@@ -0,0 +1,4 @@
{
"title": "CAN-Transceiver Error",
"description": "Es konnte keine Verbindung zum CAN-Transceiver hergestellt werden. Prüfen Sie die Hardware auf Defekte"
}

View File

@@ -0,0 +1,4 @@
{
"title": "Keine CAN-Verbindung",
"description": "Es konnte kein CAN-Signal empfangen werden. Prüfen sie die Verbindung und die Einstellungen"
}

View File

@@ -0,0 +1,4 @@
{
"title": "Config-Validierung",
"description": "Ein oder mehrer Einstellungswerte sind ausserhalb plausibler Werte. Prüfen Sie Ihre Einstellungen"
}

View File

@@ -0,0 +1,4 @@
{
"title": "Ölvorrat niedrig",
"description": "Ölvorrat ist unter der Warnschwelle. Den Ölvorrat demnächst auffüllen und im Menu 'Wartung' zurück setzen"
}

View File

@@ -0,0 +1,4 @@
{
"title": "kein EEPROM gefunden",
"description": "Es wurde kein EEPROM gefunden. Dies lässt einen Hardware-Defekt vermuten."
}

View File

@@ -0,0 +1,4 @@
{
"title": "EEPROM CFG Checksumme",
"description": "Die Checksumme der Config-Partition des EEPROM ist ungültig. Setzen sie den EEPROM-Bereich 'CFG' im Menu 'Wartung' zurück"
}

View File

@@ -0,0 +1,4 @@
{
"title": "EEPROM PDS Checksumme",
"description": "Die Checksumme der Betriebsdaten-Partition des EEPROM ist ungültig. Setzen sie den EEPROM-Bereich 'PDS' im Menu 'Wartung' zurück"
}

View File

@@ -0,0 +1,4 @@
{
"title": "EEPROM PDS Adresse",
"description": "Die Adresse der Betriebsdaten-Partition im EEPROM ist ungültig. Setzen sie den EEPROM-Bereich 'PDS' im Menu 'Wartung' zurück"
}

View File

@@ -0,0 +1,4 @@
{
"title": "EEPROM Version falsch",
"description": "Die Layout-Version des EEPROM stimmt nicht mit der Firmware-Version überein. Setzen sie den EEPROM-Bereich 'CFG' im Menu 'Wartung' zurück"
}

View File

@@ -0,0 +1,4 @@
{
"title": "Flashstorage Fehler",
"description": "Der Flashstorage konnte nicht initialisiert werden. Aktualisieren sie Flash & Firmware"
}

View File

@@ -0,0 +1,4 @@
{
"title": "Flashstorage Version falsch",
"description": "Die Version des Flashstorage stimmt nicht mit der Firmware-Version überein. Aktualisieren sie den Flash mit der passenden Update-Datei"
}

View File

@@ -0,0 +1 @@
1.3

View File

@@ -26,21 +26,36 @@ upload_speed = 921600
build_flags =
!python git_rev_macro.py
-DWIFI_SSID=${wifi_cred.wifi_ssid}
-DWIFI_PASSWORD=${wifi_cred.wifi_password}
-DWIFI_SSID_CLIENT=${wifi_cred.wifi_ssid_client}
-DWIFI_PASSWORD_CLIENT=${wifi_cred.wifi_password_client}
-DADMIN_PASSWORD=${wifi_cred.admin_password}
-DWIFI_AP_PASSWORD=${wifi_cred.wifi_ap_password}
-DWIFI_AP_IP_GW=10,0,0,1
-DATOMIC_FS_UPDATE
-DFEATURE_ENABLE_WIFI_CLIENT
-DFEATURE_ENABLE_OLED
-DFEATURE_ENABLE_CAN
;-DFEATURE_ENABLE_GPS
-DFEATURE_ENABLE_WEBSOCKETS
-DPCB_REV=4
;-DNO_MODE_FLASH
;build_type = debug
board_build.filesystem = littlefs
extra_scripts = post:prepare_littlefs.py
monitor_filters = esp8266_exception_decoder
monitor_speed = 115200
board_build.ldscript = eagle.flash.4m.ld
board_build.ldscript = eagle.flash.4m1m.ld
lib_ldf_mode = deep
lib_deps =
olikraus/U8g2 @ ^2.28.8
joaolopesf/RemoteDebug @ ^2.1.2
fastled/FastLED @ ^3.5.0
https://github.com/FastLED/FastLED.git#3d2ab78 ;fastled/FastLED @ ^3.5.0
sstaub/Ticker @ ^4.2.0
s00500/ESPUI @ ^2.0.0
coryjfowler/mcp_can @ ^1.5.0
robtillaart/I2C_EEPROM @ ^1.5.2
mikalhart/TinyGPSPlus @ ^1.0.3
me-no-dev/ESP Async WebServer @ ^1.2.3
bblanchon/ArduinoJson @ ^6.19.4

View File

@@ -0,0 +1,116 @@
# SCRIPT TO GZIP CRITICAL FILES FOR ACCELERATED WEBSERVING
# see also https://community.platformio.org/t/question-esp32-compress-files-in-data-to-gzip-before-upload-possible-to-spiffs/6274/10
import glob
import shutil
import gzip
import os
Import("env")
Import("projenv")
def gzip_file(src_path, dst_path):
with open(src_path, 'rb') as src, gzip.open(dst_path, 'wb') as dst:
for chunk in iter(lambda: src.read(4096), b""):
dst.write(chunk)
def getListOfFiles(dirName):
# create a list of file and sub directories
# names in the given directory
listOfFile = os.listdir(dirName)
allFiles = list()
# Iterate over all the entries
for entry in listOfFile:
# Create full path
fullPath = os.path.join(dirName, entry)
# If entry is a directory then get the list of files in this directory
if os.path.isdir(fullPath):
allFiles = allFiles + getListOfFiles(fullPath)
else:
allFiles.append(fullPath)
return allFiles
def remove_prefix(text, prefix):
if text.startswith(prefix):
return text[len(prefix):]
return text # or whatever
# Compress files from 'data_src/' to 'data/'
def gzip_webfiles(source, target, env):
# Filetypes to compress
filetypes_to_gzip = ['.css', '.png', '.js', '.ico', '.woff2', '.json']
print('\nGZIP: Starting gzip-Process for LittleFS-Image...\n')
data_src_dir_path = os.path.join(env.get('PROJECT_DIR'), 'data_src')
data_dir_path = env.get('PROJECT_DATA_DIR')
# check if data and datasrc exist. If the first exists and not the second, it renames it
if(os.path.exists(data_dir_path) and not os.path.exists(data_src_dir_path)):
print('GZIP: Directory "'+data_dir_path +
'" exists, "'+data_src_dir_path+'" is not found.')
print('GZIP: Renaming "' + data_dir_path +
'" to "' + data_src_dir_path + '"')
os.rename(data_dir_path, data_src_dir_path)
# Delete the 'data' directory
if(os.path.exists(data_dir_path)):
print('GZIP: Deleting the "data" directory ' + data_dir_path)
shutil.rmtree(data_dir_path)
# Recreate empty 'data' directory
print('GZIP: Re-creating an empty data directory ' + data_dir_path)
os.mkdir(data_dir_path)
# Determine the files to compress
files_to_copy = []
files_to_gzip = []
all_data_src = getListOfFiles(data_src_dir_path)
for file in all_data_src:
file_name, file_extension = os.path.splitext(file)
print(file_name + " has filetype " + file_extension)
if file_extension in filetypes_to_gzip:
files_to_gzip.append(file)
else:
filename_subdir = remove_prefix(file, data_src_dir_path)
files_to_copy.append(filename_subdir)
for file in files_to_copy:
print('GZIP: Copying file from: ' + data_src_dir_path + file + ' to: ' + data_dir_path + file)
os.makedirs(os.path.dirname(data_dir_path + file), exist_ok=True)
shutil.copy(data_src_dir_path + file, data_dir_path + file)
# Compress and move files
was_error = False
try:
for source_file_path in files_to_gzip:
print('GZIP: compressing... ' + source_file_path)
filename_subdir = remove_prefix(source_file_path, data_src_dir_path)
target_file_path = data_dir_path + filename_subdir
os.makedirs(os.path.dirname(target_file_path), exist_ok=True)
print('GZIP: Compressed... ' + target_file_path)
gzip_file(source_file_path, target_file_path + ".gz")
except IOError as e:
was_error = True
print('GZIP: Failed to compress file: ' + source_file_path)
# print( 'GZIP: EXCEPTION... {}'.format( e ) )
if was_error:
print('GZIP: Failure/Incomplete.\n')
else:
print('GZIP: Compressed correctly.\n')
return
def gzip_binffiles(source, target, env):
littlefsbin = target[0].get_abspath()
targetbin = os.path.join(os.path.dirname(littlefsbin), 'filesystem.fs')
shutil.copyfile(littlefsbin, targetbin)
gzip_file(targetbin, os.path.join(str(targetbin) + '.gz'))
os.remove(targetbin)
return
# IMPORTANT, this needs to be added to call the routine
env.AddPreAction('$BUILD_DIR/littlefs.bin', gzip_webfiles)
env.AddPostAction('$BUILD_DIR/littlefs.bin', gzip_binffiles)

49
Software/src/can.cpp Normal file
View File

@@ -0,0 +1,49 @@
#ifdef FEATURE_ENABLE_CAN
#include "can.h"
MCP_CAN CAN0(GPIO_CS_CAN);
void Init_CAN()
{
if (CAN0.begin(MCP_STDEXT, CAN_500KBPS, MCP_16MHZ) != CAN_OK)
MaintainDTC(DTC_CAN_TRANSCEIVER_FAILED, DTC_CRITICAL, true);
CAN0.init_Mask(0, 0, 0x07FF0000); // Init first mask...
CAN0.init_Mask(1, 0, 0x07FF0000); // Init second mask...
CAN0.init_Filt(0, 0, 0x012D0000); // Init first filter...
CAN0.setMode(MCP_NORMAL);
}
uint32_t Process_CAN_WheelSpeed()
{
#define FACTOR_RWP_KMH_890ADV 18 // Divider to convert Raw Data to km/h
can_frame canMsg;
static uint32_t lastRecTimestamp = 0;
uint16_t RearWheelSpeed_raw;
uint32_t milimeters_to_add = 0;
if (CAN0.readMsgBuf(&canMsg.can_id, &canMsg.can_dlc, canMsg.data) == CAN_OK)
{
RearWheelSpeed_raw = (uint16_t)canMsg.data[5] << 8 | canMsg.data[6];
// raw / FACTOR_RWP_KMH_890ADV -> km/h * 100000 -> cm/h / 3600 -> cm/s
// raw * 500 -> cm/s
uint32_t RWP_millimeter_per_second = (((uint32_t)RearWheelSpeed_raw * 1000000) / FACTOR_RWP_KMH_890ADV) / 3600;
uint32_t timesincelast = millis() - lastRecTimestamp;
lastRecTimestamp = millis();
milimeters_to_add = (RWP_millimeter_per_second * timesincelast) / 1000;
}
if (lastRecTimestamp != 0)
{
MaintainDTC(DTC_NO_CAN_SIGNAL, DTC_CRITICAL, (millis() > lastRecTimestamp + 10000 ? true : false));
}
return milimeters_to_add;
}
#endif

21
Software/src/can.h Normal file
View File

@@ -0,0 +1,21 @@
#ifndef _CAN_H_
#define _CAN_H_
#include <Arduino.h>
#include <mcp_can.h>
#include <SPI.h>
#include "common.h"
#include "globals.h"
#include "dtc.h"
struct can_frame
{
unsigned long can_id;
uint8_t can_dlc;
uint8_t data[8] __attribute__((aligned(8)));
};
void Init_CAN();
uint32_t Process_CAN_WheelSpeed();
#endif

52
Software/src/common.h Normal file
View File

@@ -0,0 +1,52 @@
#ifndef _COMMON_H_
#define _COMMON_H_
#define Q(x) #x
#define QUOTE(x) Q(x)
#if PCB_REV == 2
#define GPIO_BUTTON D7
#define GPIO_LED D8
#define GPIO_TRIGGER D6
#define GPIO_PUMP D5
#elif PCB_REV == 1 || PCB_REV == 3
#define GPIO_BUTTON D5
#define GPIO_LED D6
#define GPIO_TRIGGER D4
#define GPIO_PUMP D3
#elif PCB_REV == 4
#define GPIO_BUTTON D4
#define GPIO_LED D3
#define GPIO_TRIGGER D6
#define GPIO_PUMP D0
#define GPIO_CS_CAN D8
#endif
#ifndef HOST_NAME
#define HOST_NAME "ChainLube_%06X" // Use printf-Formatting - Chip-ID (uin32_t) will be added
#endif
#define SW_VERSION 1.4
#define FLASH_FS_VERSION 1.4
#ifndef OTA_DELAY
#define OTA_DELAY 50 // ticks -> 10ms / tick
#endif
#define LUBE_PULSE_LENGHT_MS 160
#define LUBE_PULSE_PAUSE_MS 340
// -> 2Hz PumpPulse
// -> 49,7cc / h @ 2Hz
// -> 49,7 ml / h @ 2Hz
// -> 828,4µl / min @ 2Hz
// -> 828,3µl / 60s
// -> 13,81µl / 1s
// -> 6,90µl / Pulse
#define DEFAULT_PUMP_DOSE 7
#define STARTUP_DELAY 5000
#define SHUTDOWN_DELAY_MS 5000
#endif

358
Software/src/config.cpp Normal file
View File

@@ -0,0 +1,358 @@
#include "config.h"
#include "debugger.h"
I2C_eeprom ee(0x50, EEPROM_SIZE_BYTES);
LubeConfig_t LubeConfig;
persistenceData_t PersistenceData;
const uint16_t eeVersion = 2; // inc
boolean eeAvailable = false;
const uint16_t startofLubeConfig = 16;
const uint16_t startofPersistence = 16 + sizeof(LubeConfig) + (sizeof(LubeConfig) % 16);
boolean checkEEPROMavailable();
void InitEEPROM()
{
ee.begin();
checkEEPROMavailable();
}
void EEPROM_Process()
{
switch (globals.requestEEAction)
{
case EE_CFG_SAVE:
StoreConfig_EEPROM();
globals.requestEEAction = EE_IDLE;
Debug_pushMessage("Stored EEPROM CFG\n");
break;
case EE_CFG_LOAD:
GetConfig_EEPROM();
globals.requestEEAction = EE_IDLE;
Debug_pushMessage("Loaded EEPROM CFG\n");
break;
case EE_CFG_FORMAT:
FormatConfig_EEPROM();
globals.requestEEAction = EE_IDLE;
globals.systemStatus = sysStat_Shutdown;
Debug_pushMessage("Formated EEPROM CFG\n");
break;
case EE_PDS_SAVE:
StorePersistence_EEPROM();
globals.requestEEAction = EE_IDLE;
Debug_pushMessage("Stored EEPROM PDS\n");
break;
case EE_PDS_LOAD:
GetPersistence_EEPROM();
globals.requestEEAction = EE_IDLE;
Debug_pushMessage("Loaded EEPROM PDS\n");
break;
case EE_PDS_FORMAT:
FormatPersistence_EEPROM();
globals.requestEEAction = EE_IDLE;
Debug_pushMessage("Formated EEPROM PDS\n");
break;
case EE_FORMAT_ALL:
FormatConfig_EEPROM();
FormatPersistence_EEPROM();
globals.requestEEAction = EE_IDLE;
Debug_pushMessage("Formated EEPROM ALL\n");
break;
case EE_ALL_SAVE:
StorePersistence_EEPROM();
StoreConfig_EEPROM();
globals.requestEEAction = EE_IDLE;
Debug_pushMessage("Stored EEPROM ALL\n");
break;
case EE_IDLE:
default:
globals.requestEEAction = EE_IDLE;
}
}
void StoreConfig_EEPROM()
{
LubeConfig.checksum = 0;
LubeConfig.checksum = Checksum_EEPROM((uint8_t *)&LubeConfig, sizeof(LubeConfig));
if (!checkEEPROMavailable())
return;
ee.updateBlock(startofLubeConfig, (uint8_t *)&LubeConfig, sizeof(LubeConfig));
}
void GetConfig_EEPROM()
{
if (!checkEEPROMavailable())
return;
ee.readBlock(startofLubeConfig, (uint8_t *)&LubeConfig, sizeof(LubeConfig));
uint32_t checksum = LubeConfig.checksum;
LubeConfig.checksum = 0;
if (Checksum_EEPROM((uint8_t *)&LubeConfig, sizeof(LubeConfig)) != checksum)
{
MaintainDTC(DTC_EEPROM_CFG_BAD, DTC_CRITICAL, true);
}
LubeConfig.checksum = checksum;
uint32_t ConfigSanityCheckResult = ConfigSanityCheck(false);
if (ConfigSanityCheckResult > 0)
{
MaintainDTC(DTC_EEPROM_CFG_SANITY, DTC_WARN, true, ConfigSanityCheckResult);
globals.requestEEAction = EE_CFG_SAVE;
}
}
void StorePersistence_EEPROM()
{
if (PersistenceData.writeCycleCounter >= 0xFFF0)
MovePersistencePage_EEPROM(false);
else
PersistenceData.writeCycleCounter++;
PersistenceData.checksum = 0;
PersistenceData.checksum = Checksum_EEPROM((uint8_t *)&PersistenceData, sizeof(PersistenceData));
if (!checkEEPROMavailable())
return;
ee.updateBlock(globals.eePersistanceAdress, (uint8_t *)&PersistenceData, sizeof(PersistenceData));
}
void GetPersistence_EEPROM()
{
if (!checkEEPROMavailable())
return;
ee.readBlock(0, (uint8_t *)&globals.eePersistanceAdress, sizeof(globals.eePersistanceAdress));
// if we got the StartAdress of Persistance and it's out of Range - we Reset it and store defaults
// otherwise we Read from eeprom and check if everything is correct
if (globals.eePersistanceAdress < startofPersistence || globals.eePersistanceAdress > ee.getDeviceSize())
{
MovePersistencePage_EEPROM(true);
FormatPersistence_EEPROM();
MaintainDTC(DTC_EEPROM_PDSADRESS_BAD, DTC_CRITICAL, true);
}
else
{
ee.readBlock(globals.eePersistanceAdress, (uint8_t *)&PersistenceData, sizeof(PersistenceData));
uint32_t checksum = PersistenceData.checksum;
PersistenceData.checksum = 0;
if (Checksum_EEPROM((uint8_t *)&PersistenceData, sizeof(PersistenceData)) != checksum)
{
MaintainDTC(DTC_EEPROM_PDS_BAD, DTC_CRITICAL, true);
}
PersistenceData.checksum = checksum;
}
}
void FormatConfig_EEPROM()
{
Debug_pushMessage("Formatting Config-Partition\n");
LubeConfig = LubeConfig_defaults;
LubeConfig.EEPROM_Version = eeVersion;
StoreConfig_EEPROM();
}
void FormatPersistence_EEPROM()
{
Debug_pushMessage("Formatting Persistance-Partition\n");
PersistenceData = {0};
// memset(&PersistenceData, 0, sizeof(PersistenceData));
StorePersistence_EEPROM();
}
void MovePersistencePage_EEPROM(boolean reset)
{
if (!checkEEPROMavailable())
return;
globals.eePersistanceAdress = +sizeof(PersistenceData);
PersistenceData.writeCycleCounter = 0;
// check if we reached the End of the EEPROM and Startover at the beginning
if ((globals.eePersistanceAdress + sizeof(PersistenceData)) > ee.getDeviceSize() || reset)
{
globals.eePersistanceAdress = startofPersistence;
}
ee.updateBlock(0, (uint8_t *)&globals.eePersistanceAdress, sizeof(globals.eePersistanceAdress));
}
uint32_t Checksum_EEPROM(uint8_t const *data, size_t len)
{
if (data == NULL)
return 0;
uint32_t crc, mask;
crc = 0xFFFFFFFF;
while (len--)
{
crc ^= *data++;
for (uint8_t k = 0; k < 8; k++)
{
mask = -(crc & 1);
crc = (crc >> 1) ^ (0xEDB88320 & mask);
}
}
return ~crc;
}
void dumpEEPROM(uint16_t memoryAddress, uint16_t length)
{
#define BLOCK_TO_LENGTH 16
if (!checkEEPROMavailable())
return;
char ascii_buf[BLOCK_TO_LENGTH + 1];
sprintf(ascii_buf, "%*s", BLOCK_TO_LENGTH, "ASCII");
Debug_pushMessage(PSTR("\nAddress "));
for (int x = 0; x < BLOCK_TO_LENGTH; x++)
Debug_pushMessage("%3d", x);
memoryAddress = memoryAddress / BLOCK_TO_LENGTH * BLOCK_TO_LENGTH;
length = (length + BLOCK_TO_LENGTH - 1) / BLOCK_TO_LENGTH * BLOCK_TO_LENGTH;
for (unsigned int i = 0; i < length; i++)
{
int blockpoint = memoryAddress % BLOCK_TO_LENGTH;
if (blockpoint == 0)
{
ascii_buf[BLOCK_TO_LENGTH] = 0;
Debug_pushMessage(" %s", ascii_buf);
Debug_pushMessage("\n0x%05X:", memoryAddress);
}
ascii_buf[blockpoint] = ee.readByte(memoryAddress);
Debug_pushMessage(" %02X", ascii_buf[blockpoint]);
if (ascii_buf[blockpoint] < 0x20 || ascii_buf[blockpoint] > 0x7E)
ascii_buf[blockpoint] = '.';
memoryAddress++;
}
Debug_pushMessage("\n");
}
boolean checkEEPROMavailable()
{
if (!ee.isConnected())
{
MaintainDTC(DTC_NO_EEPROM_FOUND, DTC_CRITICAL, true);
return false;
}
MaintainDTC(DTC_NO_EEPROM_FOUND, DTC_CRITICAL, false);
return true;
}
uint32_t ConfigSanityCheck(bool autocorrect)
{
uint32_t setting_reset_bits = 0;
if (!(LubeConfig.DistancePerLube_Default > 0) || !(LubeConfig.DistancePerLube_Default < 50000))
{
setting_reset_bits = setting_reset_bits | (1 << 0);
if (autocorrect)
LubeConfig.DistancePerLube_Default = LubeConfig_defaults.DistancePerLube_Default;
}
if (!(LubeConfig.DistancePerLube_Rain > 0) || !(LubeConfig.DistancePerLube_Rain < 50000))
{
setting_reset_bits = setting_reset_bits | (1 << 1);
if (autocorrect)
LubeConfig.DistancePerLube_Rain = LubeConfig_defaults.DistancePerLube_Rain;
}
if (!(LubeConfig.tankCapacity_ml > 0) || !(LubeConfig.tankCapacity_ml < 5000))
{
setting_reset_bits = setting_reset_bits | (1 << 2);
if (autocorrect)
LubeConfig.tankCapacity_ml = LubeConfig_defaults.tankCapacity_ml;
}
if (!(LubeConfig.amountPerDose_microL > 0) || !(LubeConfig.amountPerDose_microL < 100))
{
setting_reset_bits = setting_reset_bits | (1 << 3);
if (autocorrect)
LubeConfig.amountPerDose_microL = LubeConfig_defaults.amountPerDose_microL;
}
if (!(LubeConfig.TankRemindAtPercentage >= 0) || !(LubeConfig.TankRemindAtPercentage <= 100))
{
setting_reset_bits = setting_reset_bits | (1 << 4);
if (autocorrect)
LubeConfig.TankRemindAtPercentage = LubeConfig_defaults.TankRemindAtPercentage;
}
if (!(LubeConfig.PulsePerRevolution > 0) || !(LubeConfig.PulsePerRevolution < 1000))
{
setting_reset_bits = setting_reset_bits | (1 << 5);
if (autocorrect)
LubeConfig.PulsePerRevolution = LubeConfig_defaults.PulsePerRevolution;
}
if (!(LubeConfig.TireWidth_mm > 0) || !(LubeConfig.TireWidth_mm < 500))
{
setting_reset_bits = setting_reset_bits | (1 << 6);
if (autocorrect)
LubeConfig.TireWidth_mm = LubeConfig_defaults.TireWidth_mm;
}
if (!(LubeConfig.TireWidthHeight_Ratio > 0) || !(LubeConfig.TireWidthHeight_Ratio < 150))
{
setting_reset_bits = setting_reset_bits | (1 << 7);
if (autocorrect)
LubeConfig.TireWidthHeight_Ratio = LubeConfig_defaults.TireWidthHeight_Ratio;
}
if (!(LubeConfig.RimDiameter_Inch > 0) || !(LubeConfig.RimDiameter_Inch < 30))
{
setting_reset_bits = setting_reset_bits | (1 << 8);
if (autocorrect)
LubeConfig.RimDiameter_Inch = LubeConfig_defaults.RimDiameter_Inch;
}
if (!(LubeConfig.DistancePerRevolution_mm > 0) || !(LubeConfig.DistancePerRevolution_mm < 10000))
{
setting_reset_bits = setting_reset_bits | (1 << 9);
if (autocorrect)
LubeConfig.DistancePerRevolution_mm = LubeConfig_defaults.DistancePerRevolution_mm;
}
if (!(LubeConfig.BleedingPulses > 0) || !(LubeConfig.BleedingPulses < 1001))
{
setting_reset_bits = setting_reset_bits | (1 << 10);
if (autocorrect)
LubeConfig.BleedingPulses = LubeConfig_defaults.BleedingPulses;
}
if (!(LubeConfig.SpeedSource >= 0) || !(LubeConfig.SpeedSource < SpeedSourceString_Elements))
{
setting_reset_bits = setting_reset_bits | (1 << 11);
if (autocorrect)
LubeConfig.SpeedSource = LubeConfig_defaults.SpeedSource;
}
#ifdef FEATURE_ENABLE_GPS
if (!(LubeConfig.GPSBaudRate >= 0) || !(LubeConfig.GPSBaudRate < GPSBaudRateString_Elements))
{
setting_reset_bits = setting_reset_bits | (1 << 12);
if (autocorrect)
LubeConfig.GPSBaudRate = LubeConfig_defaults.GPSBaudRate;
}
#endif
#ifdef FEATURE_ENABLE_CAN
if (!(LubeConfig.CANSource >= 0) || !(LubeConfig.CANSource < CANSourceString_Elements))
{
setting_reset_bits = setting_reset_bits | (1 << 13);
if (autocorrect)
LubeConfig.CANSource = LubeConfig_defaults.CANSource;
}
#endif
return setting_reset_bits;
}

128
Software/src/config.h Normal file
View File

@@ -0,0 +1,128 @@
#ifndef _CONFIG_H_
#define _CONFIG_H_
#include <Arduino.h>
#include <Wire.h>
#include <I2C_eeprom.h>
#include "globals.h"
#include "dtc.h"
#include "common.h"
#if PCB_REV == 1 || PCB_REV == 2 || PCB_REV == 3
#define EEPROM_SIZE_BYTES I2C_DEVICESIZE_24LC64
#elif PCB_REV == 4
#define EEPROM_SIZE_BYTES I2C_DEVICESIZE_24LC256
#endif
typedef enum SpeedSource_e
{
SOURCE_TIME,
SOURCE_IMPULSE,
#ifdef FEATURE_ENABLE_GPS
SOURCE_GPS,
#endif
#if FEATURE_ENABLE_CAN
SOURCE_CAN
#endif
} SpeedSource_t;
const char SpeedSourceString[][8] = {
"Timer",
"Impuls",
#ifdef FEATURE_ENABLE_GPS
"GPS",
#endif
#if FEATURE_ENABLE_CAN
"CAN-Bus"
#endif
};
#ifdef FEATURE_ENABLE_GPS
typedef enum GPSBaudRate_e
{
BAUD_9600,
BAUD_115200
} GPSBaudRate_t;
const char GPSBaudRateString[][7] = {
"9600",
"115200"};
const size_t GPSBaudRateString_Elements = sizeof(GPSBaudRateString) / sizeof(GPSBaudRateString[0]);
#endif
#ifdef FEATURE_ENABLE_CAN
typedef enum CANSource_e
{
KTM_890_ADV_R_2021
} CANSource_t;
const char CANSourceString[][28] = {
"KTM 890 Adventure R (2021)"};
const char CANSourceString_Elements = sizeof(CANSourceString) / sizeof(CANSourceString[0]);
#endif
const size_t SpeedSourceString_Elements = sizeof(SpeedSourceString) / sizeof(SpeedSourceString[0]);
typedef struct
{
uint16_t writeCycleCounter = 0;
uint32_t tankRemain_microL = 0;
uint32_t TravelDistance_highRes_mm = 0;
uint32_t odometer_mm = 0;
uint32_t odometer = 0;
uint32_t checksum = 0;
} persistenceData_t;
typedef struct
{
uint8_t EEPROM_Version = 0;
uint32_t DistancePerLube_Default = 8000;
uint32_t DistancePerLube_Rain = 4000;
uint32_t tankCapacity_ml = 320;
uint32_t amountPerDose_microL = DEFAULT_PUMP_DOSE;
uint8_t TankRemindAtPercentage = 30;
uint8_t PulsePerRevolution = 1;
uint32_t TireWidth_mm = 150;
uint32_t TireWidthHeight_Ratio = 70;
uint32_t RimDiameter_Inch = 18;
uint32_t DistancePerRevolution_mm = 2000;
uint16_t BleedingPulses = 25;
SpeedSource_t SpeedSource = SOURCE_IMPULSE;
#ifdef FEATURE_ENABLE_GPS
GPSBaudRate_t GPSBaudRate = BAUD_115200;
#endif
#ifdef FEATURE_ENABLE_CAN
CANSource_t CANSource = KTM_890_ADV_R_2021;
#endif
uint32_t checksum = 0;
} LubeConfig_t;
const LubeConfig_t LubeConfig_defaults = {
0, 8000, 4000, 320, DEFAULT_PUMP_DOSE, 30, 1, 150, 70, 18, 2000, 25, SOURCE_IMPULSE,
#ifdef FEATURE_ENABLE_GPS
BAUD_115200,
#endif
#ifdef FEATURE_ENABLE_CAN
KTM_890_ADV_R_2021,
#endif
0};
void InitEEPROM();
void EEPROM_Process();
void StoreConfig_EEPROM();
void GetConfig_EEPROM();
void StorePersistence_EEPROM();
void GetPersistence_EEPROM();
void FormatConfig_EEPROM();
void FormatPersistence_EEPROM();
uint32_t Checksum_EEPROM(uint8_t const *data, size_t len);
void dumpEEPROM(uint16_t memoryAddress, uint16_t length);
void MovePersistencePage_EEPROM(boolean reset);
uint32_t ConfigSanityCheck(bool autocorrect = false);
extern LubeConfig_t LubeConfig;
extern persistenceData_t PersistenceData;
extern uint16_t eePersistenceMarker;
#endif // _CONFIG_H_

253
Software/src/debugger.cpp Normal file
View File

@@ -0,0 +1,253 @@
#include "debugger.h"
DebugStatus_t DebuggerStatus[dbg_cntElements];
String IpAddress2String(const IPAddress &ipAddress);
void processCmdDebug();
void Debug_formatCFG();
void Debug_formatPersistence();
void Debug_printSystemInfo();
void Debug_printWifiInfo();
void Debug_CheckEEPOM();
void Debug_dumpConfig();
void Debug_dumpPersistance();
void Debug_ShowDTCs();
void Debug_dumpGlobals();
void initDebugger()
{
DebuggerStatus[dbg_Serial] = disabled;
DebuggerStatus[dbg_Webui] = disabled;
Serial.setDebugOutput(false);
}
void SetDebugportStatus(DebugPorts_t port, DebugStatus_t status)
{
if (status == disabled)
Debug_pushMessage("disable DebugPort %s", sDebugPorts[port]);
DebuggerStatus[port] = status;
if (status == enabled)
Debug_pushMessage("enabled DebugPort %s", sDebugPorts[port]);
}
void Debug_pushMessage(const char *format, ...)
{
if ((DebuggerStatus[dbg_Serial] == enabled) || (DebuggerStatus[dbg_Webui] == enabled))
{
char buff[64];
va_list arg;
va_start(arg, format);
vsnprintf(buff, sizeof(buff), format, arg);
va_end(arg);
if (DebuggerStatus[dbg_Serial] == enabled)
{
Serial.print(buff);
}
if (DebuggerStatus[dbg_Webui] == enabled)
{
Websocket_PushLiveDebug(String(buff));
}
}
}
void pushCANDebug(uint32_t id, uint8_t dlc, uint8_t *data)
{
if ((DebuggerStatus[dbg_Serial] == enabled) || (DebuggerStatus[dbg_Webui] == enabled))
{
char buff[100];
char *p = buff;
p += snprintf(p, sizeof(buff), "CAN: 0x%08X | %d | ", id, dlc);
for (int i = 0; i < dlc; i++)
{
p += snprintf(p, sizeof(buff) - (p - buff), "%02X ", data[i]);
}
*(p++) = '\n';
*p = '\0';
if (DebuggerStatus[dbg_Serial] == enabled)
{
Serial.print(buff);
}
if (DebuggerStatus[dbg_Webui] == enabled)
{
Websocket_PushLiveDebug(String(buff));
}
}
}
void processCmdDebug(String command)
{
if (command == "sysinfo")
Debug_printSystemInfo();
else if (command == "netinfo")
Debug_printWifiInfo();
else if (command == "formatCFG")
Debug_formatCFG();
else if (command == "formatPDS")
Debug_formatPersistence();
else if (command == "checkEE")
Debug_CheckEEPOM();
else if (command == "dumpEE1k")
dumpEEPROM(0, 1024);
else if (command == "dumpEE")
dumpEEPROM(0, EEPROM_SIZE_BYTES);
else if (command == "resetPageEE")
MovePersistencePage_EEPROM(true);
else if (command == "dumpCFG")
Debug_dumpConfig();
else if (command == "dumpPDS")
Debug_dumpPersistance();
else if (command == "saveEE")
globals.requestEEAction = EE_ALL_SAVE;
else if (command == "showdtc")
Debug_ShowDTCs();
else if (command == "dumpGlobals")
Debug_dumpGlobals();
}
void Debug_formatCFG()
{
Debug_pushMessage("Formatting Config-EEPROM and reseting to default");
FormatConfig_EEPROM();
}
void Debug_formatPersistence()
{
Debug_pushMessage("Formatting Persistence-EEPROM and reseting to default");
FormatPersistence_EEPROM();
}
void RemotDebug_printSystemInfo()
{
Debug_pushMessage("Souko's ChainOiler Mk1");
Debug_pushMessage("Hostname: %s", globals.DeviceName);
FlashMode_t ideMode = ESP.getFlashChipMode();
Debug_pushMessage("Sdk version: %s", ESP.getSdkVersion());
Debug_pushMessage("Core Version: %s", ESP.getCoreVersion().c_str());
Debug_pushMessage("Boot Version: %u", ESP.getBootVersion());
Debug_pushMessage("Boot Mode: %u", ESP.getBootMode());
Debug_pushMessage("CPU Frequency: %u MHz", ESP.getCpuFreqMHz());
Debug_pushMessage("Reset reason: %s", ESP.getResetReason().c_str());
Debug_pushMessage("Flash Size: %d", ESP.getFlashChipRealSize());
Debug_pushMessage("Flash Size IDE: %d", ESP.getFlashChipSize());
Debug_pushMessage("Flash ide mode: %s", (ideMode == FM_QIO ? "QIO" : ideMode == FM_QOUT ? "QOUT"
: ideMode == FM_DIO ? "DIO"
: ideMode == FM_DOUT ? "DOUT"
: "UNKNOWN"));
Debug_pushMessage("OTA-Pass: %s", QUOTE(ADMIN_PASSWORD));
Debug_pushMessage("Git-Revison: %s", GIT_REV);
Debug_pushMessage("Sw-Version: %s", QUOTE(SW_VERSION));
}
void Debug_dumpConfig()
{
Debug_pushMessage("DistancePerLube_Default: %d", LubeConfig.DistancePerLube_Default);
Debug_pushMessage("DistancePerLube_Rain: %d", LubeConfig.DistancePerLube_Rain);
Debug_pushMessage("tankCapacity_ml: %d", LubeConfig.tankCapacity_ml);
Debug_pushMessage("amountPerDose_microL: %d", LubeConfig.amountPerDose_microL);
Debug_pushMessage("TankRemindAtPercentage: %d", LubeConfig.TankRemindAtPercentage);
Debug_pushMessage("PulsePerRevolution: %d", LubeConfig.PulsePerRevolution);
Debug_pushMessage("TireWidth_mm: %d", LubeConfig.TireWidth_mm);
Debug_pushMessage("TireWidthHeight_Ratio: %d", LubeConfig.TireWidth_mm);
Debug_pushMessage("RimDiameter_Inch: %d", LubeConfig.RimDiameter_Inch);
Debug_pushMessage("DistancePerRevolution_mm: %d", LubeConfig.DistancePerRevolution_mm);
Debug_pushMessage("BleedingPulses: %d", LubeConfig.BleedingPulses);
Debug_pushMessage("SpeedSource: %d", LubeConfig.SpeedSource);
#ifdef FEATURE_ENABLE_GPS
Debug_pushMessage("GPSBaudRate: %d", LubeConfig.GPSBaudRate);
#endif
#ifdef FEATURE_ENABLE_CAN
Debug_pushMessage("CANSource: %d", LubeConfig.CANSource);
#endif
Debug_pushMessage("checksum: 0x%08X", LubeConfig.checksum);
}
void Debug_dumpGlobals()
{
Debug_pushMessage("systemStatus: %d", globals.systemStatus);
Debug_pushMessage("resumeStatus: %d", globals.resumeStatus);
Debug_pushMessage("systemStatustxt: %s", globals.systemStatustxt);
Debug_pushMessage("purgePulses: %d", globals.purgePulses);
Debug_pushMessage("requestEEAction: %d", globals.requestEEAction);
Debug_pushMessage("DeviceName: %s", globals.DeviceName);
Debug_pushMessage("FlashVersion: %s", globals.FlashVersion);
Debug_pushMessage("eePersistanceAdress: %d", globals.eePersistanceAdress);
Debug_pushMessage("TankPercentage: %d", globals.TankPercentage);
Debug_pushMessage("hasDTC: %d", globals.hasDTC);
}
void Debug_dumpPersistance()
{
Debug_pushMessage("writeCycleCounter: %d", PersistenceData.writeCycleCounter);
Debug_pushMessage("tankRemain_microL: %d", PersistenceData.tankRemain_microL);
Debug_pushMessage("TravelDistance_highRes_mm: %d", PersistenceData.TravelDistance_highRes_mm);
Debug_pushMessage("checksum: %d", PersistenceData.checksum);
Debug_pushMessage("PSD Adress: 0x%04X", globals.eePersistanceAdress);
}
void Debug_printWifiInfo()
{
}
void Debug_CheckEEPOM()
{
uint32_t checksum = PersistenceData.checksum;
PersistenceData.checksum = 0;
if (Checksum_EEPROM((uint8_t *)&PersistenceData, sizeof(PersistenceData)) == checksum)
{
Debug_pushMessage("PersistenceData EEPROM Checksum OK\n");
}
else
{
Debug_pushMessage("PersistenceData EEPROM Checksum BAD\n");
}
PersistenceData.checksum = checksum;
checksum = LubeConfig.checksum;
LubeConfig.checksum = 0;
if (Checksum_EEPROM((uint8_t *)&LubeConfig, sizeof(LubeConfig)) == checksum)
{
Debug_pushMessage("LubeConfig EEPROM Checksum OK\n");
}
else
{
Debug_pushMessage("LubeConfig EEPROM Checksum BAD\n");
}
LubeConfig.checksum = checksum;
}
void Debug_ShowDTCs()
{
char buff_timestamp[16]; // Format: DD-hh:mm:ss:xxx
char buff_active[9];
for (uint32_t i = 0; i < MAX_DTC_STORAGE; i++)
{
if (DTCStorage[i].Number < DTC_LAST_DTC)
{
sprintf(buff_timestamp, "%02d-%02d:%02d:%02d:%03d",
DTCStorage[i].timestamp / 86400000, // Days
DTCStorage[i].timestamp / 360000 % 24, // Hours
DTCStorage[i].timestamp / 60000 % 60, // Minutes
DTCStorage[i].timestamp / 1000 % 60, // Seconds
DTCStorage[i].timestamp % 1000); // milliseconds
if (DTCStorage[i].active == DTC_ACTIVE)
strcpy(buff_active, "active");
else if (DTCStorage[i].active == DTC_PREVIOUS)
strcpy(buff_active, "previous");
else
strcpy(buff_active, "none");
Debug_pushMessage("%s \t %6d \t %s \t %d", buff_timestamp, DTCStorage[i].Number, buff_active, DTCStorage[i].severity);
}
}
}

45
Software/src/debugger.h Normal file
View File

@@ -0,0 +1,45 @@
#ifndef _DEBUGGER_H_
#define _DEBUGGER_H_
#include <Arduino.h>
#include "webui.h"
const char helpCmd[] = "sysinfo - System Info\r\n"
"netinfo - WiFi Info\r\n"
"formatPDS - Format Persistence EEPROM Data\r\n"
"formatCFG - Format Configuration EEPROM Data\r\n"
"checkEE - Check EEPROM with checksum\r\n"
"dumpEE1k - dump the first 1kb of EEPROM to Serial\r\n"
"dumpEE - dump the whole EPPROM to Serial\r\n"
"resetPageEE - Reset the PersistenceData Page\r\n"
"dumpCFG - print Config struct\r\n"
"dumpPDS - print PersistanceStruct\r\n"
"saveEE - save EE-Data\r\n"
"showdtc - Show all DTCs\r\n"
"dumpGlobals - print globals\r\n";
typedef enum DebugStatus_e
{
disabled,
enabled
} DebugStatus_t;
typedef enum DebugPorts_e
{
dbg_Serial,
dbg_Webui,
dbg_cntElements
} DebugPorts_t;
const char sDebugPorts[dbg_cntElements][7] = {
"Serial",
"WebUI"};
extern DebugStatus_t DebuggerStatus[dbg_cntElements];
void initDebugger();
void pushCANDebug(uint32_t id, uint8_t dlc, uint8_t *data);
void Debug_pushMessage(const char *format, ...);
void SetDebugportStatus(DebugPorts_t port, DebugStatus_t status);
#endif

139
Software/src/dtc.cpp Normal file
View File

@@ -0,0 +1,139 @@
#include "dtc.h"
#include "debugger.h"
DTCEntry_s DTCStorage[MAX_DTC_STORAGE];
void MaintainDTC(DTCNums_t DTC_no, DTCSeverity_t DTC_severity, boolean active, uint32_t DebugValue)
{
for (int i = 0; i < MAX_DTC_STORAGE; i++)
{
if (DTCStorage[i].Number == DTC_no)
{
if (active && DTCStorage[i].active != DTC_ACTIVE)
{
Debug_pushMessage("DTC gone active: %d, DebugVal: %d\n", DTC_no, DebugValue);
DTCStorage[i].timestamp = millis();
DTCStorage[i].active = DTC_ACTIVE;
DTCStorage[i].severity = DTC_severity;
DTCStorage[i].debugVal = DebugValue;
}
if (!active && DTCStorage[i].active == DTC_ACTIVE)
{
Debug_pushMessage("DTC gone previous: %d\n", DTC_no);
DTCStorage[i].active = DTC_PREVIOUS;
}
return;
}
}
// DTC was not found with upper iteration, but is active
// so we need to look for free space to store DTC
if (active == true)
{
for (int i = 0; i < MAX_DTC_STORAGE; i++)
{
if (DTCStorage[i].Number == DTC_LAST_DTC)
{
Debug_pushMessage("new DTC registered: %d, DebugVal: %d\n", DTC_no, DebugValue);
DTCStorage[i].Number = DTC_no;
DTCStorage[i].timestamp = millis();
DTCStorage[i].active = DTC_ACTIVE;
DTCStorage[i].debugVal = DebugValue;
DTCStorage[i].severity = DTC_severity;
return;
}
}
}
}
void ClearDTC(DTCNums_t DTC_no)
{
for (int i = 0; i < MAX_DTC_STORAGE; i++)
{
if (DTCStorage[i].Number == DTC_no)
{
DTCStorage[i].Number = DTC_LAST_DTC;
DTCStorage[i].active = DTC_NONE;
DTCStorage[i].timestamp = 0;
}
}
}
void ClearAllDTC()
{
for (int i = 0; i < MAX_DTC_STORAGE; i++)
{
DTCStorage[i].Number = DTC_LAST_DTC;
DTCStorage[i].active = DTC_NONE;
DTCStorage[i].timestamp = 0;
}
}
DTCNums_t getlastDTC(boolean only_active)
{
int8_t pointer = -1;
uint32_t lasttimestamp = 0;
for (int i = 0; i < MAX_DTC_STORAGE; i++)
{
if (DTCStorage[i].Number > 0 && DTCStorage[i].timestamp > lasttimestamp)
{
if (only_active == false || DTCStorage[i].active == DTC_ACTIVE)
{
pointer = i;
lasttimestamp = DTCStorage[i].timestamp;
}
}
}
return pointer >= 0 ? DTCStorage[pointer].Number : DTC_LAST_DTC;
}
DTCNums_t getlastDTC_Severity(boolean only_active, DTCSeverity_t severity)
{
int8_t pointer = -1;
uint32_t lasttimestamp = 0;
for (int i = 0; i < MAX_DTC_STORAGE; i++)
{
if (DTCStorage[i].Number > 0 && DTCStorage[i].timestamp > lasttimestamp)
{
if ((only_active == false || DTCStorage[i].active == DTC_ACTIVE) && DTCStorage[i].severity == severity)
{
pointer = i;
lasttimestamp = DTCStorage[i].timestamp;
}
}
}
return pointer >= 0 ? DTCStorage[pointer].Number : DTC_LAST_DTC;
}
void DTC_Process()
{
static tSystem_Status preserverSysStatusError;
if (getlastDTC(false) < DTC_LAST_DTC)
{
globals.hasDTC = true;
if (getlastDTC_Severity(true, DTC_CRITICAL) < DTC_LAST_DTC)
{
if (globals.systemStatus != sysStat_Error)
{
preserverSysStatusError = globals.systemStatus;
}
globals.systemStatus = sysStat_Error;
}
else
{
if (globals.systemStatus == sysStat_Error)
{
globals.systemStatus = preserverSysStatusError;
}
}
}
else
{
globals.hasDTC = false;
}
}

61
Software/src/dtc.h Normal file
View File

@@ -0,0 +1,61 @@
#ifndef _DTC_H_
#define _DTC_H_
#include <Arduino.h>
#define MAX_DTC_STORAGE 6
typedef enum DTCNums_e
{
DTC_TANK_EMPTY = 1,
DTC_TANK_LOW,
DTC_NO_EEPROM_FOUND,
DTC_EEPROM_CFG_BAD,
DTC_EEPROM_PDS_BAD,
DTC_EEPROM_PDSADRESS_BAD,
DTC_EEPROM_VERSION_BAD,
DTC_FLASHFS_ERROR,
DTC_FLASHFS_VERSION_ERROR,
#ifdef FEATURE_ENABLE_GPS
DTC_NO_GPS_SERIAL,
#endif
#ifdef FEATURE_ENABLE_CAN
DTC_CAN_TRANSCEIVER_FAILED,
DTC_NO_CAN_SIGNAL,
#endif
DTC_EEPROM_CFG_SANITY,
DTC_LAST_DTC
} DTCNums_t;
typedef enum DTCActive_e
{
DTC_NONE,
DTC_ACTIVE,
DTC_PREVIOUS
} DTCActive_t;
typedef enum DTCSeverity_e
{
DTC_INFO,
DTC_WARN,
DTC_CRITICAL
} DTCSeverity_t;
typedef struct DTCEntry_s
{
DTCNums_t Number;
uint32_t timestamp;
DTCActive_t active;
DTCSeverity_t severity;
uint32_t debugVal;
} DTCEntry_t;
void MaintainDTC(DTCNums_t DTC_no, DTCSeverity_t DTC_severity, boolean active, uint32_t DebugValue = 0);
void ClearDTC(DTCNums_t DTC_no);
void ClearAllDTC();
DTCNums_t getlastDTC(boolean only_active);
DTCNums_t getlastDTC_Severity(boolean only_active, DTCSeverity_t severity);
void DTC_Process();
extern DTCEntry_s DTCStorage[MAX_DTC_STORAGE];
#endif

11
Software/src/globals.cpp Normal file
View File

@@ -0,0 +1,11 @@
#include "globals.h"
Globals_t globals;
void initGlobals()
{
globals.purgePulses = 0;
globals.requestEEAction = EE_IDLE;
globals.resumeStatus = sysStat_Normal;
globals.systemStatus = sysStat_Startup;
}

48
Software/src/globals.h Normal file
View File

@@ -0,0 +1,48 @@
#ifndef _GLOBALS_H_
#define _GLOBALS_H_
#include <Arduino.h>
typedef enum eSystem_Status
{
sysStat_Startup,
sysStat_Normal,
sysStat_Rain,
sysStat_Purge,
sysStat_Error,
sysStat_Shutdown
} tSystem_Status;
typedef enum eEERequest
{
EE_IDLE,
EE_CFG_SAVE,
EE_CFG_LOAD,
EE_CFG_FORMAT,
EE_PDS_SAVE,
EE_PDS_LOAD,
EE_PDS_FORMAT,
EE_FORMAT_ALL,
EE_ALL_SAVE
} tEERequest;
typedef struct Globals_s
{
tSystem_Status systemStatus = sysStat_Startup;
tSystem_Status resumeStatus = sysStat_Startup;
char systemStatustxt[16] = "";
uint16_t purgePulses = 0;
eEERequest requestEEAction = EE_IDLE;
char DeviceName[33];
char FlashVersion[10];
uint16_t eePersistanceAdress;
uint8_t TankPercentage;
bool hasDTC;
} Globals_t;
extern Globals_t globals;
void initGlobals();
#endif

59
Software/src/gps.cpp Normal file
View File

@@ -0,0 +1,59 @@
#ifdef FEATURE_ENABLE_GPS
#include "gps.h"
TinyGPSPlus gps;
void Init_GPS()
{
uint32_t baudrate;
switch (LubeConfig.GPSBaudRate)
{
case BAUD_9600:
baudrate = 9600;
break;
case BAUD_115200:
baudrate = 115200;
break;
default:
baudrate = 4800;
break;
}
Debug_pushMessage(PSTR("Init GPS with Baud %d\n"), baudrate);
Serial.begin(baudrate);
}
uint32_t Process_GPS_WheelSpeed()
{
static uint32_t lastRecTimestamp;
static uint32_t lastValidSpeedTimestamp;
uint16_t RearWheelSpeed_kmh;
while (Serial.available() > 0)
{
uint8_t incoming = Serial.read();
if (gps.encode(incoming))
{
RearWheelSpeed_kmh = gps.speed.isValid() ? gps.speed.kmph() : 0;
if (RearWheelSpeed_kmh > 0)
{
uint32_t RWP_millimeter_per_second = ((uint32_t)RearWheelSpeed_kmh * 1000000) / 3600;
uint32_t timesincelast = millis() - lastValidSpeedTimestamp;
lastValidSpeedTimestamp = millis();
uint32_t milimeters_to_add = (RWP_millimeter_per_second * timesincelast) / 1000;
return milimeters_to_add;
}
lastRecTimestamp = millis();
}
}
MaintainDTC(DTC_NO_GPS_SERIAL,DTC_CRITICAL, (millis() > lastRecTimestamp + 10000));
return 0;
}
#endif

12
Software/src/gps.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef _GPS_H_
#define _GPS_H_
#include <TinyGPSPlus.h>
#include "config.h"
#include "common.h"
#include "dtc.h"
void Init_GPS();
uint32_t Process_GPS_WheelSpeed();
#endif

107
Software/src/lubeapp.cpp Normal file
View File

@@ -0,0 +1,107 @@
#include "lubeapp.h"
uint32_t lubePulseTimestamp = 0;
void RunLubeApp(uint32_t add_milimeters)
{
globals.TankPercentage = PersistenceData.tankRemain_microL / (LubeConfig.tankCapacity_ml * 10);
MaintainDTC(DTC_TANK_EMPTY, DTC_CRITICAL, (PersistenceData.tankRemain_microL < LubeConfig.amountPerDose_microL));
MaintainDTC(DTC_TANK_LOW, DTC_WARN, (globals.TankPercentage < LubeConfig.TankRemindAtPercentage));
// Add traveled Distance in mm
PersistenceData.TravelDistance_highRes_mm += add_milimeters;
PersistenceData.odometer_mm += add_milimeters;
if (PersistenceData.odometer_mm >= 1000000)
{
PersistenceData.odometer++;
PersistenceData.odometer_mm = 0;
}
switch (globals.systemStatus)
{
case sysStat_Startup:
if (millis() > STARTUP_DELAY)
{
globals.systemStatus = sysStat_Normal;
globals.resumeStatus = sysStat_Normal;
}
break;
case sysStat_Normal:
if (PersistenceData.TravelDistance_highRes_mm / 1000 > LubeConfig.DistancePerLube_Default)
{
LubePulse();
PersistenceData.TravelDistance_highRes_mm = 0;
}
break;
case sysStat_Rain:
if (PersistenceData.TravelDistance_highRes_mm / 1000 > LubeConfig.DistancePerLube_Rain)
{
LubePulse();
PersistenceData.TravelDistance_highRes_mm = 0;
}
break;
case sysStat_Purge:
if (globals.purgePulses > 0)
{
if (lubePulseTimestamp + LUBE_PULSE_PAUSE_MS < millis())
{
LubePulse();
globals.purgePulses--;
}
}
else
{
globals.systemStatus = globals.resumeStatus;
}
break;
case sysStat_Error:
case sysStat_Shutdown:
default:
break;
}
switch (globals.systemStatus)
{
case sysStat_Normal:
strcpy_P(globals.systemStatustxt, PSTR("Normal"));
break;
case sysStat_Purge:
strcpy_P(globals.systemStatustxt, PSTR("Purge"));
break;
case sysStat_Rain:
strcpy_P(globals.systemStatustxt, PSTR("Rain"));
break;
case sysStat_Startup:
strcpy_P(globals.systemStatustxt, PSTR("Startup"));
break;
case sysStat_Error:
strcpy_P(globals.systemStatustxt, PSTR("Error"));
break;
case sysStat_Shutdown:
strcpy_P(globals.systemStatustxt, PSTR("Shutdown"));
break;
}
// maintain Pin-State of Lube-Pump
if (lubePulseTimestamp > millis())
digitalWrite(GPIO_PUMP, HIGH);
else
digitalWrite(GPIO_PUMP, LOW);
}
void LubePulse()
{
if (PersistenceData.tankRemain_microL > 0) // Only Lube if theres Oil remaining!
{
lubePulseTimestamp = millis() + LUBE_PULSE_LENGHT_MS;
if (PersistenceData.tankRemain_microL < LubeConfig.amountPerDose_microL) // Prevent underrun and shiftover
PersistenceData.tankRemain_microL = 0;
else
PersistenceData.tankRemain_microL = PersistenceData.tankRemain_microL - LubeConfig.amountPerDose_microL;
}
}

View File

@@ -2,14 +2,13 @@
#define _LUBEAPP_H_
#include <Arduino.h>
#include <stdlib.h>
#include "config.h"
#include "common.h"
#include "globals.h"
#include "dtc.h"
#define LUBE_PULSE_LENGHT_MS 250
#define LUBE_PULSE_PAUSE_MS 250
void RunLubeApp(volatile uint32_t * wheelPulseCounter);
void RunLubeApp(uint32_t add_milimeters);
void LubePulse();
#endif

588
Software/src/main.cpp Normal file
View File

@@ -0,0 +1,588 @@
#include <Arduino.h>
#include <Wire.h>
#ifdef FEATURE_ENABLE_OLED
#include <U8g2lib.h>
#endif
#include <ESP8266WiFi.h>
#include <ArduinoOTA.h>
#include <FastLED.h>
#include <Ticker.h>
#include "common.h"
#include "sanitycheck.h"
#include "lubeapp.h"
#include "webui.h"
#include "config.h"
#include "globals.h"
#include "debugger.h"
#ifdef FEATURE_ENABLE_CAN
#include "can.h"
#endif
#ifdef FEATURE_ENABLE_GPS
#include "gps.h"
#endif
#include "dtc.h"
#ifdef FEATURE_ENABLE_WIFI_CLIENT
#include <ESP8266WiFiMulti.h>
const char *ssid = QUOTE(WIFI_SSID_CLIENT);
const char *password = QUOTE(WIFI_PASSWORD_CLIENT);
const uint32_t connectTimeoutMs = 5000;
ESP8266WiFiMulti wifiMulti;
#endif
bool startSetupMode = false;
volatile uint32_t wheel_pulse = 0;
CRGB leds[1];
// Function-Prototypes
void IRAM_ATTR trigger_ISR();
void LED_Process(uint8_t override = false, CRGB setColor = CRGB::White);
#ifdef FEATURE_ENABLE_OLED
U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(-1);
void Display_Process();
#endif
void Button_Process();
void toggleWiFiAP(boolean shutdown = false);
void SystemShutdown();
uint32_t Process_Impulse_WheelSpeed();
void EEPROMCyclicPDS_callback();
#ifdef FEATURE_ENABLE_WIFI_CLIENT
void wifiMaintainConnectionTicker_callback();
Ticker WiFiMaintainConnectionTicker(wifiMaintainConnectionTicker_callback, 1000, 0, MILLIS);
#endif
Ticker EEPROMCyclicPDSTicker(EEPROMCyclicPDS_callback, 60000, 0, MILLIS);
void setup()
{
system_update_cpu_freq(SYS_CPU_80MHZ);
snprintf(globals.DeviceName, 32, HOST_NAME, ESP.getChipId());
WiFi.persistent(false);
ClearAllDTC(); // Init DTC-Storage
#ifdef FEATURE_ENABLE_WIFI_CLIENT
WiFi.mode(WIFI_STA);
WiFi.setHostname(globals.DeviceName);
wifiMulti.addAP(QUOTE(WIFI_SSID_CLIENT), QUOTE(WIFI_PASSWORD_CLIENT));
WiFiMaintainConnectionTicker.start();
#else
WiFi.mode(WIFI_OFF);
#endif
Serial.begin(115200);
Serial.println("\n\nSouko's ChainLube Mk1");
Serial.println(globals.DeviceName);
InitEEPROM();
GetConfig_EEPROM();
GetPersistence_EEPROM();
#ifdef FEATURE_ENABLE_OLED
u8x8.begin();
u8x8.setFont(u8x8_font_chroma48medium8_r);
#endif
FastLED.addLeds<WS2811, GPIO_LED, RGB>(leds, 1); // GRB ordering is assumed
switch (LubeConfig.SpeedSource)
{
case SOURCE_IMPULSE:
pinMode(GPIO_TRIGGER, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(GPIO_TRIGGER), trigger_ISR, FALLING);
break;
#ifdef FEATURE_ENABLE_GPS
case SOURCE_GPS:
Init_GPS();
break;
#endif
case SOURCE_TIME:
break;
#ifdef FEATURE_ENABLE_CAN
case SOURCE_CAN:
Init_CAN();
break;
#endif
default:
Debug_pushMessage("Source Setting N/A");
break;
}
pinMode(GPIO_BUTTON, INPUT_PULLUP);
pinMode(GPIO_PUMP, OUTPUT);
ArduinoOTA.setPort(8266);
ArduinoOTA.setHostname(globals.DeviceName);
ArduinoOTA.setPassword(QUOTE(ADMIN_PASSWORD));
#ifdef FEATURE_ENABLE_OLED
ArduinoOTA.onStart([]()
{
u8x8.clearDisplay();
u8x8.drawString(0, 0, "OTA-Update");
u8x8.refreshDisplay(); });
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total)
{
static bool refreshed = false;
if (!refreshed)
{
u8x8.clearDisplay();
refreshed = true;
u8x8.drawString(0, 0, "OTA Upload");
}
uint32_t percent = progress / (total / 100);
u8x8.setCursor(0, 1);
u8x8.printf("%d %%", percent);
u8x8.refreshDisplay(); });
ArduinoOTA.onEnd([]()
{
u8x8.clearDisplay();
u8x8.drawString(0, 0, "OTA-Restart");
u8x8.refreshDisplay(); });
#endif
ArduinoOTA.begin();
#ifdef FEATURE_ENABLE_OLED
u8x8.clearDisplay();
u8x8.drawString(0, 0, "KTM ChainLube V1");
u8x8.refreshDisplay();
#endif
initWebUI();
initGlobals();
EEPROMCyclicPDSTicker.start();
Serial.println("Setup Done");
}
void loop()
{
uint32_t wheelDistance = 0;
switch (LubeConfig.SpeedSource)
{
case SOURCE_IMPULSE:
wheelDistance = Process_Impulse_WheelSpeed();
break;
#ifdef FEATURE_ENABLE_CAN
case SOURCE_CAN:
wheelDistance = Process_CAN_WheelSpeed();
break;
#endif
case SOURCE_TIME:
break;
#ifdef FEATURE_ENABLE_GPS
case SOURCE_GPS:
wheelDistance = Process_GPS_WheelSpeed();
break;
#endif
}
RunLubeApp(wheelDistance);
#ifdef FEATURE_ENABLE_OLED
Display_Process();
#endif
Button_Process();
LED_Process();
EEPROM_Process();
Webserver_Process();
DTC_Process();
ArduinoOTA.handle();
EEPROMCyclicPDSTicker.update();
#ifdef FEATURE_ENABLE_WIFI_CLIENT
WiFiMaintainConnectionTicker.update();
#endif
if (globals.systemStatus == sysStat_Shutdown)
SystemShutdown();
yield();
}
String IpAddress2String(const IPAddress &ipAddress)
{
return String(ipAddress[0]) + String(".") +
String(ipAddress[1]) + String(".") +
String(ipAddress[2]) + String(".") +
String(ipAddress[3]);
}
#ifdef FEATURE_ENABLE_WIFI_CLIENT
void wifiMaintainConnectionTicker_callback()
{
static uint32_t WiFiFailCount = 0;
const uint32_t WiFiFailMax = 20;
if (wifiMulti.run(connectTimeoutMs) == WL_CONNECTED)
{
return;
}
else
{
if (WiFiFailCount < WiFiFailMax)
{
WiFiFailCount++;
}
else
{
Debug_pushMessage("WiFi not connected! - Start AP");
toggleWiFiAP();
}
}
}
#endif
void EEPROMCyclicPDS_callback()
{
StorePersistence_EEPROM();
}
void trigger_ISR()
{
wheel_pulse++;
}
void LED_Process(uint8_t override, CRGB SetColor)
{
typedef enum
{
LED_Startup,
LED_Normal,
LED_Confirm_Normal,
LED_Rain,
LED_Confirm_Rain,
LED_Purge,
LED_Error,
LED_Override
} tLED_Status;
static tSystem_Status oldSysStatus = sysStat_Startup;
static tLED_Status LED_Status = LED_Startup;
static CRGB LED_override_color = 0;
static tLED_Status LED_ResumeOverrideStatus = LED_Startup;
uint8_t color = 0;
uint32_t timer = 0;
static uint32_t timestamp = 0;
timer = millis();
if (override == 1)
{
if (LED_Status != LED_Override)
{
LED_ResumeOverrideStatus = LED_Status;
Debug_pushMessage("Override LED_Status");
}
LED_Status = LED_Override;
LED_override_color = SetColor;
}
if (override == 2)
{
if (LED_Status == LED_Override)
{
LED_Status = LED_ResumeOverrideStatus;
Debug_pushMessage("Resume LED_Status");
}
}
if (oldSysStatus != globals.systemStatus)
{
switch (globals.systemStatus)
{
case sysStat_Startup:
LED_Status = LED_Startup;
Debug_pushMessage("sysStat: Startup");
break;
case sysStat_Normal:
timestamp = timer + 3500;
LED_Status = LED_Confirm_Normal;
Debug_pushMessage("sysStat: Normal");
break;
case sysStat_Rain:
timestamp = timer + 3500;
LED_Status = LED_Confirm_Rain;
Debug_pushMessage("sysStat: Rain");
break;
case sysStat_Purge:
LED_Status = LED_Purge;
Debug_pushMessage("sysStat: Purge");
break;
case sysStat_Error:
LED_Status = LED_Error;
Debug_pushMessage("sysStat: Error");
break;
case sysStat_Shutdown:
default:
break;
}
oldSysStatus = globals.systemStatus;
}
switch (LED_Status)
{
case LED_Startup:
FastLED.setBrightness(255);
if (globals.TankPercentage < LubeConfig.TankRemindAtPercentage)
leds[0] = CRGB::OrangeRed;
else
leds[0] = CRGB::White;
break;
case LED_Confirm_Normal:
FastLED.setBrightness(255);
leds[0] = timer % 250 > 125 ? CRGB(0, 255, 0) : CRGB(0, 4, 0);
if (timestamp < timer)
{
LED_Status = LED_Normal;
FastLED.setBrightness(64);
Debug_pushMessage("LED_Status: Confirm -> Normal");
}
break;
case LED_Normal:
#ifndef NO_MODE_FLASH
if (timer % 2000 > 1950)
leds[0] = CRGB(0, 255, 0);
else if (WiFi.getMode() != WIFI_OFF && timer % 2000 > 1800 && timer % 2000 < 1850)
#else
if (WiFi.getMode() != WIFI_OFF && timer % 2000 > 1950)
#endif
leds[0] = CRGB(255, 128, 0);
else
leds[0] = CRGB(0, 4, 0);
break;
case LED_Confirm_Rain:
FastLED.setBrightness(255);
leds[0] = timer % 250 > 125 ? CRGB(0, 0, 255) : CRGB(0, 0, 4);
if (timestamp < timer)
{
LED_Status = LED_Rain;
FastLED.setBrightness(64);
Debug_pushMessage("LED_Status: Confirm -> Rain");
}
break;
case LED_Rain:
#ifndef NO_MODE_FLASH
if (timer % 2000 > 1950)
leds[0] = CRGB(0, 0, 255);
else if (WiFi.getMode() != WIFI_OFF && timer % 2000 > 1800 && timer % 2000 < 1850)
#else
if (WiFi.getMode() != WIFI_OFF && timer % 2000 > 1950)
#endif
leds[0] = CRGB(255, 128, 0);
else
leds[0] = CRGB(0, 0, 4);
break;
case LED_Purge:
timer = timer % 500;
color = timer / 2;
leds[0] = CRGB::DeepPink;
if (timer < 250)
FastLED.setBrightness(color);
else
FastLED.setBrightness(250 - color);
break;
case LED_Error:
leds[0] = timer % 500 > 250 ? CRGB::Red : CRGB::Black;
break;
case LED_Override:
leds[0] = LED_override_color;
break;
default:
break;
}
FastLED.show();
}
#ifdef FEATURE_ENABLE_OLED
void Display_Process()
{
static tSystem_Status oldSysStatus = sysStat_Startup;
if (oldSysStatus != globals.systemStatus)
{
u8x8.clearDisplay();
u8x8.drawString(0, 0, "KTM ChainLube V1");
oldSysStatus = globals.systemStatus;
}
u8x8.setCursor(0, 1);
uint32_t DistRemain = globals.systemStatus == sysStat_Normal ? LubeConfig.DistancePerLube_Default : LubeConfig.DistancePerLube_Rain;
DistRemain = DistRemain - (PersistenceData.TravelDistance_highRes_mm / 1000);
u8x8.printf(PSTR("Mode: %10s\n"), globals.systemStatustxt);
if (globals.systemStatus == sysStat_Error)
{
u8x8.printf(PSTR("last DTC: %6d\n"), getlastDTC(false));
}
else
{
u8x8.printf(PSTR("next Lube: %4dm\n"), DistRemain);
u8x8.printf(PSTR("Tank: %8dml\n"), PersistenceData.tankRemain_microL / 1000);
u8x8.printf(PSTR("WiFi: %10s\n"), (WiFi.getMode() == WIFI_AP ? "AP" : WiFi.getMode() == WIFI_OFF ? "OFF"
: WiFi.getMode() == WIFI_STA ? "CLIENT"
: "UNKNOWN"));
u8x8.printf(PSTR("Source: %8s\n"), SpeedSourceString[LubeConfig.SpeedSource]);
u8x8.printf("%s\n", WiFi.localIP().toString().c_str());
}
u8x8.refreshDisplay();
}
#endif
void Button_Process()
{
#define BUTTON_ACTION_DELAY_TOGGLEMODE 500
#define BUTTON_ACTION_DELAY_PURGE 3500
#define BUTTON_ACTION_DELAY_WIFI 6500
#define BUTTON_ACTION_DELAY_NOTHING 9500
typedef enum buttonAction_e
{
BTN_INACTIVE,
BTN_NOTHING,
BTN_TOGGLEMODE,
BTN_TOGGLEWIFI,
BTN_STARTPURGE
} buttonAction_t;
static uint32_t buttonTimestamp = 0;
static buttonAction_t buttonAction = BTN_INACTIVE;
if (digitalRead(GPIO_BUTTON) == LOW)
{
if (buttonTimestamp == 0)
buttonTimestamp = millis();
if (buttonTimestamp + BUTTON_ACTION_DELAY_NOTHING < millis())
{
LED_Process(1, CRGB::White);
buttonAction = BTN_NOTHING;
}
else if (buttonTimestamp + BUTTON_ACTION_DELAY_WIFI < millis())
{
LED_Process(1, CRGB::Yellow);
buttonAction = BTN_TOGGLEWIFI;
}
else if (buttonTimestamp + BUTTON_ACTION_DELAY_PURGE < millis())
{
LED_Process(1, CRGB::DeepPink);
buttonAction = BTN_STARTPURGE;
}
else if (buttonTimestamp + BUTTON_ACTION_DELAY_TOGGLEMODE < millis())
{
CRGB color = globals.systemStatus == sysStat_Normal ? CRGB::Blue : CRGB::Green;
LED_Process(1, color);
buttonAction = BTN_TOGGLEMODE;
}
}
else
{
if (buttonAction != BTN_INACTIVE)
{
switch (buttonAction)
{
case BTN_TOGGLEWIFI:
toggleWiFiAP();
Debug_pushMessage("Starting WiFi AP");
break;
case BTN_STARTPURGE:
globals.systemStatus = sysStat_Purge;
globals.purgePulses = LubeConfig.BleedingPulses;
Debug_pushMessage("Starting Purge");
break;
case BTN_TOGGLEMODE:
switch (globals.systemStatus)
{
case sysStat_Normal:
globals.systemStatus = sysStat_Rain;
globals.resumeStatus = sysStat_Rain;
break;
case sysStat_Rain:
globals.systemStatus = sysStat_Normal;
globals.resumeStatus = sysStat_Normal;
break;
default:
break;
}
Debug_pushMessage("Toggling Mode");
break;
case BTN_NOTHING:
default:
Debug_pushMessage("Nothing or invalid");
break;
}
LED_Process(2);
}
buttonAction = BTN_INACTIVE;
buttonTimestamp = 0;
}
}
void toggleWiFiAP(boolean shutdown)
{
if (WiFi.getMode() != WIFI_OFF)
{
WiFi.mode(WIFI_OFF);
Debug_pushMessage("WiFi turned off");
#ifdef FEATURE_ENABLE_WIFI_CLIENT
WiFiMaintainConnectionTicker.stop();
#endif
}
else
{
WiFi.mode(WIFI_AP);
WiFi.softAPConfig(IPAddress(WIFI_AP_IP_GW), IPAddress(WIFI_AP_IP_GW), IPAddress(255, 255, 255, 0));
WiFi.softAP(globals.DeviceName, QUOTE(WIFI_AP_PASSWORD));
#ifdef FEATURE_ENABLE_WIFI_CLIENT
WiFiMaintainConnectionTicker.stop();
Debug_pushMessage("WiFi AP started, stopped Maintain-Timer");
#else
Debug_pushMessage("WiFi AP started");
#endif
}
}
void SystemShutdown()
{
static uint32_t shutdown_delay = 0;
if (shutdown_delay == 0)
{
shutdown_delay = millis() + SHUTDOWN_DELAY_MS;
Serial.printf("Shutdown requested - Restarting in %d seconds\n", SHUTDOWN_DELAY_MS / 1000);
}
if (shutdown_delay < millis())
{
StoreConfig_EEPROM();
StorePersistence_EEPROM();
ESP.restart();
}
}
uint32_t Process_Impulse_WheelSpeed()
{
uint32_t add_milimeters;
// Calculate traveled Distance in mm
add_milimeters = (wheel_pulse * (LubeConfig.DistancePerRevolution_mm / LubeConfig.PulsePerRevolution));
wheel_pulse = 0;
return add_milimeters;
}

View File

@@ -0,0 +1,37 @@
#ifndef _SANITYCHECK_H_
#define _SANITYCHECK_H_
#ifndef PCB_REV
#error "You must define PCB_REV"
#else
#if PCB_REV < 1 || PCB_REV > 4
#error "Unsupported PCB-Revision"
#endif
#if PCB_REV < 4 && defined(FEATURE_ENABLE_CAN)
#error "CAN-Feature unsupported with this PCB-Rev"
#endif
#if PCB_REV < 4 && defined(DFEATURE_ENABLE_GPS)
#error "CAN-Feature unsupported with this PCB-Rev"
#endif
#endif
#ifndef ADMIN_PASSWORD
#error "You need to define ADMIN_PASSWORD for OTA-Update"
#endif
#ifdef FEATURE_ENABLE_WIFI_CLIENT
#ifndef WIFI_PASSWORD_CLIENT
#error "You must define an WIFI_PASSWORD for Client-Mode"
#endif
#ifndef WIFI_SSID_CLIENT
#error "You must define an WIFI_SSID for Client-Mode"
#endif
#endif
#ifndef WIFI_AP_PASSWORD
#error "You must define an WIFI_AP_PASSWORD for Standalone AP-Mode"
#endif
#endif //_SANITYCHECK_H_

571
Software/src/webui.cpp Normal file
View File

@@ -0,0 +1,571 @@
#include "webui.h"
AsyncWebServer webServer(80);
const char *PARAM_MESSAGE = "message";
String processor(const String &var);
void WebserverPOST_Callback(AsyncWebServerRequest *request);
void WebserverNotFound_Callback(AsyncWebServerRequest *request);
void Webserver_Callback(AsyncWebServerRequest *request);
void WebserverFirmwareUpdate_Callback(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final);
void WebserverEERestore_Callback(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final);
void WebServerEEJSON_Callback(AsyncWebServerRequest *request);
void GetFlashVersion(char *buff, size_t buff_size);
#ifdef FEATURE_ENABLE_WEBSOCKETS
AsyncWebSocket webSocket("/ws");
void WebsocketEvent_Callback(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len);
void Websocket_HandleMessage(void *arg, uint8_t *data, size_t len);
#endif
void initWebUI()
{
if (!LittleFS.begin())
{
Debug_pushMessage("An Error has occurred while mounting LittleFS\n");
MaintainDTC(DTC_FLASHFS_ERROR, DTC_CRITICAL, true);
return;
}
GetFlashVersion(globals.FlashVersion, sizeof(globals.FlashVersion));
if (strcmp(globals.FlashVersion, QUOTE(FLASH_FS_VERSION)))
{
MaintainDTC(DTC_FLASHFS_VERSION_ERROR, DTC_WARN, true);
}
MDNS.begin(globals.DeviceName);
MDNS.addService("http", "tcp", 80);
#ifdef FEATURE_ENABLE_WEBSOCKETS
webSocket.onEvent(WebsocketEvent_Callback);
webServer.addHandler(&webSocket);
#endif
webServer.serveStatic("/static/", LittleFS, "/static/").setCacheControl("max-age=360000");
webServer.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
{ request->redirect("/index.htm"); });
webServer.onNotFound(WebserverNotFound_Callback);
webServer.on("/index.htm", HTTP_GET, Webserver_Callback);
webServer.on("/post.htm", HTTP_POST, WebserverPOST_Callback);
webServer.on("/eejson", HTTP_GET, WebServerEEJSON_Callback);
webServer.on(
"/doUpdate", HTTP_POST, [](AsyncWebServerRequest *request) {}, WebserverFirmwareUpdate_Callback);
webServer.on(
"/eeRestore", HTTP_POST, [](AsyncWebServerRequest *request) {}, WebserverEERestore_Callback);
webServer.begin();
}
void Webserver_Process()
{
#ifdef FEATURE_ENABLE_WEBSOCKETS
webSocket.cleanupClients();
#endif
}
String processor(const String &var)
{
if (var == "TANK_REMAIN_CAPACITY")
return String((PersistenceData.tankRemain_microL / 10) / LubeConfig.tankCapacity_ml);
if (var == "LUBE_DISTANCE_NORMAL")
return String(LubeConfig.DistancePerLube_Default);
if (var == "LUBE_DISTANCE_RAIN")
return String(LubeConfig.DistancePerLube_Rain);
if (var == "TANK_CAPACITY")
return String(LubeConfig.tankCapacity_ml);
if (var == "AMOUNT_PER_DOSE")
return String(LubeConfig.amountPerDose_microL);
if (var == "TANK_REMIND")
return String(LubeConfig.TankRemindAtPercentage);
if (var == "PULSE_PER_REV")
return String(LubeConfig.PulsePerRevolution);
if (var == "TIRE_WIDTH_MM")
return String(LubeConfig.TireWidth_mm);
if (var == "TIRE_RATIO")
return String(LubeConfig.TireWidthHeight_Ratio);
if (var == "RIM_DIAMETER")
return String(LubeConfig.RimDiameter_Inch);
if (var == "DISTANCE_PER_REV")
return String(LubeConfig.DistancePerRevolution_mm);
if (var == "BLEEDING_PULSES")
return String(LubeConfig.BleedingPulses);
if (var == "SPEED_SOURCE")
return String(SpeedSourceString[LubeConfig.SpeedSource]);
if (var == "GPS_BAUD")
#ifdef FEATURE_ENABLE_GPS
return String(GPSBaudRateString[LubeConfig.GPSBaudRate]);
#else
return "Feature N/A";
#endif
if (var == "CAN_SOURCE")
#ifdef FEATURE_ENABLE_CAN
return String(CANSourceString[LubeConfig.CANSource]);
#else
return "Feature N/A";
#endif
if (var == "CONFIG_CHECKSUM")
{
char buffer[7];
sprintf(buffer, "0x%04X", LubeConfig.checksum);
return String(buffer);
}
if (var == "WRITE_CYCLE_COUNT")
return String(PersistenceData.writeCycleCounter);
if (var == "PERSISTENCE_MARKER")
return String(globals.eePersistanceAdress);
if (var == "TANK_REMAIN_UL")
return String(PersistenceData.tankRemain_microL);
if (var == "TRAVEL_DISTANCE_HIGHRES")
return String(PersistenceData.TravelDistance_highRes_mm);
if (var == "ODOMETER")
return String(PersistenceData.odometer);
if (var == "ODOMETER_M")
return String(PersistenceData.odometer_mm / 1000);
if (var == "PERSISTANCE_CHECKSUM")
{
char buffer[7];
sprintf(buffer, "0x%04X", PersistenceData.checksum);
return String(buffer);
}
if (var == "SHOW_IMPULSE_SETTINGS")
return LubeConfig.SpeedSource == SOURCE_IMPULSE ? "" : "hidden";
if (var == "SHOW_CAN_SETTINGS")
#ifdef FEATURE_ENABLE_CAN
return LubeConfig.SpeedSource == SOURCE_CAN ? "" : "hidden";
#else
return "hidden";
#endif
if (var == "SHOW_GPS_SETTINGS")
#ifdef FEATURE_ENABLE_GPS
return LubeConfig.SpeedSource == SOURCE_GPS ? "" : "hidden";
#else
return "hidden";
#endif
if (var == "SHOW_DTC_TABLE")
return globals.hasDTC ? "" : "hidden";
if (var == "DTC_TABLE")
{
String temp = "";
char buff_timestamp[16]; // Format: DD-hh:mm:ss:xxx
for (uint32_t i = 0; i < MAX_DTC_STORAGE; i++)
{
if (DTCStorage[i].Number < DTC_LAST_DTC)
{
sprintf(buff_timestamp, "%02d-%02d:%02d:%02d:%03d",
DTCStorage[i].timestamp / 86400000, // Days
DTCStorage[i].timestamp / 360000 % 24, // Hours
DTCStorage[i].timestamp / 60000 % 60, // Minutes
DTCStorage[i].timestamp / 1000 % 60, // Seconds
DTCStorage[i].timestamp % 1000); // milliseconds
temp = temp + "<tr data-dtc=" + String(DTCStorage[i].Number);
temp = temp + " data-debugval=" + String(DTCStorage[i].debugVal) + "><td>" + String(buff_timestamp);
temp = temp + "</td><td>" + String(DTCStorage[i].Number) + "</td><td>";
temp = temp + "<img src=static/img/";
switch (DTCStorage[i].severity)
{
case DTC_CRITICAL:
temp = temp + "critical";
break;
case DTC_WARN:
temp = temp + "warn";
break;
case DTC_INFO:
temp = temp + "info";
break;
}
temp = temp + ".png></td><td>";
if (DTCStorage[i].active == DTC_ACTIVE)
temp = temp + "active";
else if (DTCStorage[i].active == DTC_PREVIOUS)
temp = temp + "previous";
else
temp = temp + "none";
temp = temp + "</td></tr>";
}
}
return temp;
}
if (var == "SOURCE_SELECT_OPTIONS")
{
String temp;
for (uint32_t i = 0; i < SpeedSourceString_Elements; i++)
{
String selected = LubeConfig.SpeedSource == i ? " selected " : "";
temp = temp + "<option value=\"" + i + "\"" + selected + ">" + SpeedSourceString[i] + "</option>";
}
return temp;
}
#ifdef FEATURE_ENABLE_CAN
if (var == "CANSOURCE_SELECT_OPTIONS")
{
String temp;
for (uint32_t i = 0; i < CANSourceString_Elements; i++)
{
String selected = LubeConfig.CANSource == i ? " selected " : "";
temp = temp + "<option value=\"" + i + "\"" + selected + ">" + CANSourceString[i] + "</option>";
}
return temp;
}
#endif
#ifdef FEATURE_EABLE_GPS
if (var == "GPSBAUD_SELECT_OPTIONS")
{
String temp;
for (uint32_t i = 0; i < GPSBaudRateString_Elements; i++)
{
String selected = LubeConfig.GPSBaudRate == i ? " selected " : "";
temp = temp + "<option value=\"" + i + "\"" + selected + ">" + GPSBaudRateString[i] + "</option>";
}
return temp;
}
#endif
if (var == "SYSTEM_STATUS")
return String(globals.systemStatustxt);
if (var == "SW_VERSION")
{
return String(QUOTE(SW_VERSION));
}
if (var == "FS_VERSION")
return String(globals.FlashVersion);
if (var == "PLACEHOLDER")
return "placeholder";
return String();
}
void Webserver_Callback(AsyncWebServerRequest *request)
{
request->send(LittleFS, "/index.htm", "text/html", false, processor);
}
void WebserverPOST_Callback(AsyncWebServerRequest *request)
{
request->send(LittleFS, "/post.htm", "text/html", false, processor);
Debug_pushMessage("POST:\n");
int paramsNr = request->params();
for (int i = 0; i < paramsNr; i++)
{
AsyncWebParameter *p = request->getParam(i);
Debug_pushMessage("%s : %s\n", p->name().c_str(), p->value().c_str());
// begin: POST Form Source Changed
if (p->name() == "sourceselect")
{
SpeedSource_t temp = (SpeedSource_t)p->value().toInt();
Debug_pushMessage("temp: %d", temp);
Debug_pushMessage("SpeedSource: %d", LubeConfig.SpeedSource);
if (LubeConfig.SpeedSource != temp)
{
LubeConfig.SpeedSource = temp;
globals.systemStatus = sysStat_Shutdown;
}
}
// end: POST Form Source Changed
// begin: POST Form Source Pulse Settings
if (p->name() == "tirewidth")
LubeConfig.TireWidth_mm = p->value().toInt();
if (p->name() == "tireratio")
LubeConfig.TireWidthHeight_Ratio = p->value().toInt();
if (p->name() == "tiredia")
LubeConfig.RimDiameter_Inch = p->value().toInt();
if (p->name() == "pulserev")
LubeConfig.PulsePerRevolution = p->value().toInt();
if (p->name() == "pulsesave")
globals.requestEEAction = EE_CFG_SAVE;
// end: POST Form Source Pulse Settings
#ifdef FEATURE_EABLE_GPS
// begin: POST Form Source GPS Settings
if (p->name() == "gpsbaud")
LubeConfig.GPSBaudRate = (GPSBaudRate_t)p->value().toInt();
if (p->name() == "gpssave")
globals.requestEEAction = EE_CFG_SAVE;
// end: POST Form Source GPS Settings
#endif
#ifdef FEATURE_EABLE_CAN
// begin: POST Form Source CAN Settings
if (p->name() == "cansource")
LubeConfig.CANSource = (CANSource_t)p->value().toInt();
if (p->name() == "cansave")
globals.requestEEAction = EE_CFG_SAVE;
// end: POST Form Source CAN Settings
#endif
// begin: POST Form Lubrication
if (p->name() == "lubedistancenormal")
LubeConfig.DistancePerLube_Default = p->value().toInt();
if (p->name() == "lubedistancerain")
LubeConfig.DistancePerLube_Rain = p->value().toInt();
if (p->name() == "lubesave")
globals.requestEEAction = EE_CFG_SAVE;
// end: POST Form Lubrication
// begin: POST Form Oiltank
if (p->name() == "tankcap")
LubeConfig.tankCapacity_ml = p->value().toInt();
if (p->name() == "tankwarn")
LubeConfig.TankRemindAtPercentage = p->value().toInt();
if (p->name() == "pumppulse")
LubeConfig.amountPerDose_microL = p->value().toInt();
if (p->name() == "oilsave")
globals.requestEEAction = EE_CFG_SAVE;
// end: POST Form Oiltank
// begin: POST Form Maintenance
if (p->name() == "purgepulse")
LubeConfig.BleedingPulses = p->value().toInt();
if (p->name() == "maintsave")
globals.requestEEAction = EE_CFG_SAVE;
if (p->name() == "resettank")
{
PersistenceData.tankRemain_microL = LubeConfig.tankCapacity_ml * 1000;
globals.requestEEAction = EE_PDS_SAVE;
}
if (p->name() == "reset_ee_btn")
{
if (request->hasParam("reset_ee_pds", true))
{
AsyncWebParameter *param = request->getParam("reset_ee_pds", true);
if (param->value() == "on")
globals.requestEEAction = globals.requestEEAction == EE_CFG_FORMAT ? EE_FORMAT_ALL : EE_PDS_FORMAT;
}
if (request->hasParam("reset_ee_cfg", true))
{
AsyncWebParameter *param = request->getParam("reset_ee_cfg", true);
if (param->value() == "on")
globals.requestEEAction = globals.requestEEAction == EE_PDS_FORMAT ? EE_FORMAT_ALL : EE_CFG_FORMAT;
}
}
if (p->name() == "purgenow")
{
globals.systemStatus = sysStat_Purge;
globals.purgePulses = LubeConfig.BleedingPulses;
}
if (p->name() == "reboot")
{
globals.systemStatus = sysStat_Shutdown;
}
// end: POST Form Maintenance
}
}
void WebserverNotFound_Callback(AsyncWebServerRequest *request)
{
request->send(404, "text/html", "Not found");
}
void GetFlashVersion(char *buff, size_t buff_size)
{
File this_file = LittleFS.open("version", "r");
if (!this_file)
{ // failed to open the file, retrn empty result
buff[0] = '\0';
return;
}
if (this_file.available())
{
int bytes_read;
bytes_read = this_file.readBytesUntil('\r', buff, buff_size - 1);
buff[bytes_read] = '\0';
}
this_file.close();
}
void WebserverFirmwareUpdate_Callback(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final)
{
if (!index)
{
Debug_pushMessage("Update");
size_t content_len = request->contentLength();
int cmd = (filename.indexOf(".fs") > -1) ? U_FS : U_FLASH;
Update.runAsync(true);
if (!Update.begin(content_len, cmd))
{
Update.printError(Serial);
}
}
if (Update.write(data, len) != len)
{
Update.printError(Serial);
}
else
{
Debug_pushMessage("Progress: %d%%\n", (Update.progress() * 100) / Update.size());
}
if (final)
{
AsyncWebServerResponse *response = request->beginResponse(302, "text/plain", "Please wait while the device reboots");
response->addHeader("Refresh", "20");
response->addHeader("Location", "/");
request->send(response);
if (!Update.end(true))
{
Update.printError(Serial);
}
else
{
Debug_pushMessage("Update complete\n");
globals.systemStatus = sysStat_Shutdown;
}
}
}
void WebserverEERestore_Callback(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final)
{
bool ee_done = false;
bool validext = false;
if (!index)
{
Debug_pushMessage("EEPROM restore\n");
// size_t content_len = request->contentLength();
validext = (filename.indexOf(".ee.json") > -1);
}
if (validext)
{
Debug_pushMessage("Restoring EEPROM-Stuff\n");
}
if (final)
{
AsyncWebServerResponse *response = request->beginResponse(302, "text/plain", "Please wait while the device reboots");
response->addHeader("Refresh", "20");
response->addHeader("Location", "/");
request->send(response);
if (ee_done)
{
Debug_pushMessage("Update complete");
globals.systemStatus = sysStat_Shutdown;
}
else
{
}
}
}
void WebServerEEJSON_Callback(AsyncWebServerRequest *request)
{
AsyncResponseStream *response = request->beginResponseStream("application/json");
DynamicJsonDocument json(1024);
JsonObject fwinfo = json.createNestedObject("info");
char buffer[16];
fwinfo["DeviceName"] = globals.DeviceName;
fwinfo["FW-Version"] = QUOTE(SW_VERSION);
fwinfo["FS-Version"] = globals.FlashVersion;
JsonObject config = json.createNestedObject("config");
config["EEPROM_Version"] = LubeConfig.EEPROM_Version;
config["DistancePerLube_Default"] = LubeConfig.DistancePerLube_Default;
config["DistancePerLube_Rain"] = LubeConfig.DistancePerLube_Rain;
config["tankCapacity_ml"] = LubeConfig.tankCapacity_ml;
config["amountPerDose_microL"] = LubeConfig.amountPerDose_microL;
config["TankRemindAtPercentage"] = LubeConfig.TankRemindAtPercentage;
config["PulsePerRevolution"] = LubeConfig.PulsePerRevolution;
config["TireWidth_mm"] = LubeConfig.TireWidth_mm;
config["TireWidthHeight_Ratio"] = LubeConfig.TireWidthHeight_Ratio;
config["RimDiameter_Inch"] = LubeConfig.RimDiameter_Inch;
config["DistancePerRevolution_mm"] = LubeConfig.DistancePerRevolution_mm;
config["BleedingPulses"] = LubeConfig.BleedingPulses;
config["SpeedSource"] = LubeConfig.SpeedSource;
config["SpeedSource_Str"] = SpeedSourceString[LubeConfig.SpeedSource];
#ifdef FEATURE_ENABLE_GPS
config["GPSBaudRate"] = LubeConfig.GPSBaudRate;
config["GPSBaudRate_Str"] = GPSBaudRateString[LubeConfig.GPSBaudRate];
#endif
#ifdef FEATURE_ENABLE_CAN
config["CANSource"] = LubeConfig.CANSource;
config["CANSource_Str"] = CANSourceString[LubeConfig.CANSource];
#endif
sprintf(buffer, "0x%08X", LubeConfig.checksum);
config["checksum"] = buffer;
JsonObject eepart = json.createNestedObject("eepart");
sprintf(buffer, "0x%04X", globals.eePersistanceAdress);
eepart["PersistanceAddress"] = buffer;
JsonObject persis = json.createNestedObject("persis");
persis["writeCycleCounter"] = PersistenceData.writeCycleCounter;
persis["tankRemain_microL"] = PersistenceData.tankRemain_microL;
persis["TravelDistance_highRes_mm"] = PersistenceData.TravelDistance_highRes_mm;
persis["odometer_mm"] = PersistenceData.odometer_mm;
persis["odometer"] = PersistenceData.odometer;
sprintf(buffer, "0x%08X", PersistenceData.checksum);
persis["checksum"] = buffer;
serializeJsonPretty(json, *response);
response->addHeader("Content-disposition", "attachment; filename=backup.ee.json");
request->send(response);
}
#ifdef FEATURE_ENABLE_WEBSOCKETS
void WebsocketEvent_Callback(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len)
{
switch (type)
{
case WS_EVT_CONNECT:
Debug_pushMessage("WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str());
break;
case WS_EVT_DISCONNECT:
Debug_pushMessage("WebSocket client #%u disconnected\n", client->id());
break;
case WS_EVT_DATA:
Websocket_HandleMessage(arg, data, len);
break;
case WS_EVT_PONG:
case WS_EVT_ERROR:
break;
}
}
void Websocket_HandleMessage(void *arg, uint8_t *data, size_t len)
{
AwsFrameInfo *info = (AwsFrameInfo *)arg;
if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT)
{
data[len] = 0;
Debug_pushMessage("Got WebSocket Message: %s \n", (char *)data);
if (strcmp((char *)data, "start") == 0)
{
SetDebugportStatus(dbg_Webui, enabled);
}
else if (strcmp((char *)data, "stop") == 0)
{
SetDebugportStatus(dbg_Webui, disabled);
}
else if (strcmp((char *)data, "foo") == 0)
{
Debug_pushMessage("Got WebSocket Message 'foo' from client\n");
}
}
}
void Websocket_PushLiveDebug(String Message)
{
webSocket.textAll(Message + "\n");
}
#endif

27
Software/src/webui.h Normal file
View File

@@ -0,0 +1,27 @@
#ifndef _WEBUI_H_
#define _WEBUI_H_
#include <Arduino.h>
#include <FS.h>
#include <LittleFS.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <Updater.h>
#include <ESP8266mDNS.h>
#include <AsyncJson.h>
#include <ArduinoJson.h>
#include "config.h"
#include "globals.h"
#include "dtc.h"
#include "common.h"
#include "debugger.h"
void initWebUI();
void Webserver_Process();
#ifdef FEATURE_ENABLE_WEBSOCKETS
void Websocket_PushLiveDebug(String Message);
#endif
#endif

View File

@@ -1,5 +1,5 @@
[wifi_cred]
admin_password = adminpass
wifi_ap_password = wifiappass
wifi_ssid = wifi-ssid
wifi_password = wifi-pass
wifi_ssid_client = wifi-ssid
wifi_password_client = wifi-pass