141 Commits

Author SHA1 Message Date
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
87e3d2e739 Updated PCB 2022-01-12 00:51:36 +01:00
a3d5c4ef6f fixed correct Calculation of Lube-Distance 2022-01-10 23:17:07 +01:00
febb658bf8 changed pinning and added EEPROM 2022-01-10 23:16:37 +01:00
bfa4272334 Basic Function works 2022-01-10 00:55:04 +01:00
402535051a need to reset TravelDistance after LubePulse 2022-01-10 00:03:11 +01:00
829e70d11a LED-Tweak and some first Functions für LubeApp 2022-01-10 00:02:21 +01:00
ffe943f187 WebUI config stuff 2022-01-09 20:51:16 +01:00
2e3a9f6e3f Work on EEPROM-Stuff 2022-01-08 03:14:26 +01:00
435a0e1f5a Build-Defines and stuff updated 2022-01-07 23:36:02 +01:00
01ecf5a4a4 changes in ceredentials-handling of sourcefiles 2022-01-07 21:35:06 +01:00
4bffdf932e Base Software System 2022-01-07 21:02:27 +01:00
75 changed files with 114646 additions and 1808 deletions

1
.gitignore vendored Normal file
View File

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

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

3
Hardware/.gitignore vendored
View File

@@ -27,4 +27,5 @@ fp-info-cache
*.xml
*.csv
gerber/
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

View File

@@ -169,6 +169,33 @@ X 5V 9 -100 800 100 D 50 50 1 1 W
ENDDRAW
ENDDEF
#
# Memory_EEPROM_24LC64
#
DEF Memory_EEPROM_24LC64 U 0 20 Y Y 1 F N
F0 "U" -250 250 50 H V C CNN
F1 "Memory_EEPROM_24LC64" 50 250 50 H V L CNN
F2 "" 0 0 50 H I C CNN
F3 "" 0 0 50 H I C CNN
ALIAS 24LC02 24LC00 24LC04 24LC08 24LC01 24LC512 24LC64 24LC1025 24LC32 24LC256 24LC128 CAT24C256 CAT24C128
$FPLIST
DIP*W7.62mm*
SOIC*3.9x4.9mm*
TSSOP*4.4x3mm*P0.65mm*
DFN*3x2mm*P0.5mm*
$ENDFPLIST
DRAW
S -300 200 300 -200 1 1 10 f
X A0 1 -400 100 100 R 50 50 1 1 I
X A1 2 -400 0 100 R 50 50 1 1 I
X A2 3 -400 -100 100 R 50 50 1 1 I
X GND 4 0 -300 100 U 50 50 1 1 W
X SDA 5 400 100 100 L 50 50 1 1 B
X SCL 6 400 0 100 L 50 50 1 1 I
X WP 7 400 -100 100 L 50 50 1 1 I
X VCC 8 0 300 100 D 50 50 1 1 W
ENDDRAW
ENDDEF
#
# Regulator_Switching_R-78C5.0-1.0
#
DEF Regulator_Switching_R-78C5.0-1.0 U 0 10 Y Y 1 F N

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

@@ -92,8 +92,6 @@ F 3 "" H 6500 4200 50 0001 C CNN
$EndComp
Wire Wire Line
6500 4200 6500 3950
Wire Wire Line
8450 3050 8450 3750
Wire Wire Line
8450 3750 8600 3750
Wire Wire Line
@@ -218,8 +216,9 @@ U 1 1 61D2CD57
P 8250 4400
F 0 "D2" V 8296 4320 50 0000 R CNN
F 1 "BAT42" V 8205 4320 50 0000 R CNN
F 2 "Diode_THT:D_DO-35_SOD27_P7.62mm_Horizontal" H 8250 4225 50 0001 C CNN
F 2 "Diode_SMD:D_SOD-123" H 8250 4225 50 0001 C CNN
F 3 "http://www.vishay.com/docs/85660/bat42.pdf" H 8250 4400 50 0001 C CNN
F 4 "BAT 42" H 8250 4400 50 0001 C CNN "Reichelt Order No."
1 8250 4400
0 -1 -1 0
$EndComp
@@ -239,7 +238,7 @@ Wire Wire Line
Wire Wire Line
8250 4700 8250 4550
Wire Wire Line
8250 4250 8250 3150
8250 4250 8250 3350
$Comp
L Device:C C1
U 1 1 61D2F740
@@ -254,23 +253,23 @@ $EndComp
$Comp
L Device:C C2
U 1 1 61D32638
P 8000 3400
F 0 "C2" H 8115 3446 50 0000 L CNN
F 1 "10n" H 8115 3355 50 0000 L CNN
F 2 "Capacitor_SMD:C_0805_2012Metric_Pad1.18x1.45mm_HandSolder" H 8038 3250 50 0001 C CNN
F 3 "~" H 8000 3400 50 0001 C CNN
1 8000 3400
P 8000 3700
F 0 "C2" H 8115 3746 50 0000 L CNN
F 1 "10n" H 8115 3655 50 0000 L CNN
F 2 "Capacitor_SMD:C_0805_2012Metric_Pad1.18x1.45mm_HandSolder" H 8038 3550 50 0001 C CNN
F 3 "~" H 8000 3700 50 0001 C CNN
1 8000 3700
1 0 0 -1
$EndComp
$Comp
L power:GND #PWR0109
U 1 1 61D32EB9
P 8000 3550
F 0 "#PWR0109" H 8000 3300 50 0001 C CNN
F 1 "GND" H 8005 3377 50 0000 C CNN
F 2 "" H 8000 3550 50 0001 C CNN
F 3 "" H 8000 3550 50 0001 C CNN
1 8000 3550
P 8000 3850
F 0 "#PWR0109" H 8000 3600 50 0001 C CNN
F 1 "GND" H 8005 3677 50 0000 C CNN
F 2 "" H 8000 3850 50 0001 C CNN
F 3 "" H 8000 3850 50 0001 C CNN
1 8000 3850
1 0 0 -1
$EndComp
$Comp
@@ -284,32 +283,15 @@ F 3 "" H 7400 3850 50 0001 C CNN
1 7400 3850
1 0 0 -1
$EndComp
Wire Wire Line
6900 3150 8000 3150
Wire Wire Line
6900 3050 8450 3050
Wire Wire Line
7400 3250 7400 3550
Wire Wire Line
8000 3250 8000 3150
Connection ~ 8000 3150
Wire Wire Line
8000 3150 8250 3150
Wire Wire Line
6900 3250 7400 3250
Wire Wire Line
7400 3250 7750 3250
Wire Wire Line
7750 3250 7750 4250
Connection ~ 7400 3250
$Comp
L Diode:BAT42 D1
U 1 1 61D35ED1
P 7750 4400
F 0 "D1" V 7796 4320 50 0000 R CNN
F 1 "BAT42" V 7705 4320 50 0000 R CNN
F 2 "Diode_THT:D_DO-35_SOD27_P7.62mm_Horizontal" H 7750 4225 50 0001 C CNN
F 2 "Diode_SMD:D_SOD-123" H 7750 4225 50 0001 C CNN
F 3 "http://www.vishay.com/docs/85660/bat42.pdf" H 7750 4400 50 0001 C CNN
F 4 "BAT 42" H 7750 4400 50 0001 C CNN "Reichelt Order No."
1 7750 4400
0 -1 -1 0
$EndComp
@@ -319,7 +301,7 @@ U 1 1 61D36E68
P 9100 2750
F 0 "D3" H 9100 2967 50 0000 C CNN
F 1 "1N4001" H 9100 2876 50 0000 C CNN
F 2 "Diode_THT:D_DO-41_SOD81_P10.16mm_Horizontal" H 9100 2575 50 0001 C CNN
F 2 "Diode_THT:D_DO-35_SOD27_P7.62mm_Horizontal" H 9100 2575 50 0001 C CNN
F 3 "http://www.vishay.com/docs/88503/1n4001.pdf" H 9100 2750 50 0001 C CNN
1 9100 2750
1 0 0 -1
@@ -338,6 +320,7 @@ F 0 "C3" H 9568 2246 50 0000 L CNN
F 1 "470µF" H 9568 2155 50 0000 L CNN
F 2 "Capacitor_SMD:CP_Elec_8x10" H 9488 2050 50 0001 C CNN
F 3 "~" H 9450 2200 50 0001 C CNN
F 4 "ECC MZS350ARA471" H 9450 2200 50 0001 C CNN "Reichelt Order No."
1 9450 2200
1 0 0 -1
$EndComp
@@ -363,6 +346,7 @@ F 0 "C4" H 10668 2246 50 0000 L CNN
F 1 "100µ" H 10668 2155 50 0000 L CNN
F 2 "Capacitor_SMD:CP_Elec_6.3x7.7" H 10588 2050 50 0001 C CNN
F 3 "~" H 10550 2200 50 0001 C CNN
F 4 "ECC HXE250ARA101" H 10550 2200 50 0001 C CNN "Reichelt Order No."
1 10550 2200
1 0 0 -1
$EndComp
@@ -378,8 +362,6 @@ F 3 "" H 10550 2350 50 0001 C CNN
1 10550 2350
1 0 0 -1
$EndComp
Wire Wire Line
7150 3350 6900 3350
$Comp
L power:GND #PWR0113
U 1 1 61D3BF4D
@@ -423,7 +405,7 @@ U 1 1 61D3DFDC
P 9300 3350
F 0 "D4" V 9254 3430 50 0000 L CNN
F 1 "1N4001" V 9345 3430 50 0000 L CNN
F 2 "Diode_THT:D_DO-41_SOD81_P10.16mm_Horizontal" H 9300 3175 50 0001 C CNN
F 2 "Diode_THT:D_DO-35_SOD27_P7.62mm_Horizontal" H 9300 3175 50 0001 C CNN
F 3 "http://www.vishay.com/docs/88503/1n4001.pdf" H 9300 3350 50 0001 C CNN
1 9300 3350
0 1 1 0
@@ -455,8 +437,6 @@ Wire Wire Line
7850 5400 7850 5450
Wire Wire Line
7150 5300 7300 5300
Wire Wire Line
7150 3350 7150 5300
Wire Wire Line
7750 4550 7750 5200
Wire Wire Line
@@ -481,94 +461,238 @@ Wire Wire Line
$Comp
L Connector_Generic:Conn_01x04 J5
U 1 1 61E12B85
P 7650 2500
F 0 "J5" H 7730 2492 50 0000 L CNN
F 1 "I2C" H 7730 2401 50 0000 L CNN
F 2 "Connector_PinHeader_2.54mm:PinHeader_1x04_P2.54mm_Vertical" H 7650 2500 50 0001 C CNN
F 3 "~" H 7650 2500 50 0001 C CNN
1 7650 2500
P 8450 2500
F 0 "J5" H 8530 2492 50 0000 L CNN
F 1 "I2C" H 8530 2401 50 0000 L CNN
F 2 "Connector_PinHeader_2.54mm:PinHeader_1x04_P2.54mm_Horizontal" H 8450 2500 50 0001 C CNN
F 3 "~" H 8450 2500 50 0001 C CNN
1 8450 2500
1 0 0 -1
$EndComp
$Comp
L power:+3.3V #PWR0116
U 1 1 61E137AF
P 7250 1550
F 0 "#PWR0116" H 7250 1400 50 0001 C CNN
F 1 "+3.3V" H 7265 1723 50 0000 C CNN
F 2 "" H 7250 1550 50 0001 C CNN
F 3 "" H 7250 1550 50 0001 C CNN
1 7250 1550
P 8050 1550
F 0 "#PWR0116" H 8050 1400 50 0001 C CNN
F 1 "+3.3V" H 8065 1723 50 0000 C CNN
F 2 "" H 8050 1550 50 0001 C CNN
F 3 "" H 8050 1550 50 0001 C CNN
1 8050 1550
1 0 0 -1
$EndComp
$Comp
L power:GND #PWR0117
U 1 1 61E147DB
P 7400 2750
F 0 "#PWR0117" H 7400 2500 50 0001 C CNN
F 1 "GND" H 7405 2577 50 0000 C CNN
F 2 "" H 7400 2750 50 0001 C CNN
F 3 "" H 7400 2750 50 0001 C CNN
1 7400 2750
P 8200 2750
F 0 "#PWR0117" H 8200 2500 50 0001 C CNN
F 1 "GND" H 8205 2577 50 0000 C CNN
F 2 "" H 8200 2750 50 0001 C CNN
F 3 "" H 8200 2750 50 0001 C CNN
1 8200 2750
1 0 0 -1
$EndComp
Wire Wire Line
7450 2700 7400 2700
8250 2700 8200 2700
Wire Wire Line
7400 2700 7400 2750
8200 2700 8200 2750
Wire Wire Line
6900 2950 7250 2950
8050 2950 8050 2400
Wire Wire Line
7250 2950 7250 2400
8050 2400 8250 2400
Wire Wire Line
7250 2400 7450 2400
7950 2850 7950 2500
Wire Wire Line
6900 2850 7150 2850
Wire Wire Line
7150 2850 7150 2500
Wire Wire Line
7150 2500 7450 2500
7950 2500 8250 2500
$Comp
L Device:R R4
U 1 1 61E189A2
P 7150 1850
F 0 "R4" H 7080 1804 50 0000 R CNN
F 1 "4k7" H 7080 1895 50 0000 R CNN
F 2 "Resistor_SMD:R_0805_2012Metric_Pad1.20x1.40mm_HandSolder" V 7080 1850 50 0001 C CNN
F 3 "~" H 7150 1850 50 0001 C CNN
1 7150 1850
P 7950 1850
F 0 "R4" H 7880 1804 50 0000 R CNN
F 1 "4k7" H 7880 1895 50 0000 R CNN
F 2 "Resistor_SMD:R_0805_2012Metric_Pad1.20x1.40mm_HandSolder" V 7880 1850 50 0001 C CNN
F 3 "~" H 7950 1850 50 0001 C CNN
1 7950 1850
-1 0 0 1
$EndComp
$Comp
L Device:R R5
U 1 1 61E1955B
P 7250 2150
F 0 "R5" H 7180 2104 50 0000 R CNN
F 1 "4k7" H 7180 2195 50 0000 R CNN
F 2 "Resistor_SMD:R_0805_2012Metric_Pad1.20x1.40mm_HandSolder" V 7180 2150 50 0001 C CNN
F 3 "~" H 7250 2150 50 0001 C CNN
1 7250 2150
P 8050 2150
F 0 "R5" H 7980 2104 50 0000 R CNN
F 1 "4k7" H 7980 2195 50 0000 R CNN
F 2 "Resistor_SMD:R_0805_2012Metric_Pad1.20x1.40mm_HandSolder" V 7980 2150 50 0001 C CNN
F 3 "~" H 8050 2150 50 0001 C CNN
1 8050 2150
-1 0 0 1
$EndComp
Wire Wire Line
7450 2600 7050 2600
8250 2600 7850 2600
Wire Wire Line
7050 2600 7050 1600
7850 2600 7850 1600
Wire Wire Line
7050 1600 7150 1600
7850 1600 7950 1600
Wire Wire Line
7250 1600 7250 1550
Connection ~ 7150 1600
8050 1600 8050 1550
Connection ~ 7950 1600
Wire Wire Line
7150 1600 7250 1600
7950 1600 8050 1600
Wire Wire Line
7250 2000 7250 1600
Connection ~ 7250 1600
8050 2000 8050 1600
Connection ~ 8050 1600
Wire Wire Line
7150 1600 7150 1700
7950 1600 7950 1700
Wire Wire Line
7250 2300 7250 2400
Connection ~ 7250 2400
8050 2300 8050 2400
Connection ~ 8050 2400
Wire Wire Line
7150 2000 7150 2500
Connection ~ 7150 2500
7950 2000 7950 2500
Connection ~ 7950 2500
Wire Wire Line
6900 3550 7150 3550
Wire Wire Line
7150 3550 7150 5300
Wire Wire Line
6900 3450 7400 3450
Connection ~ 7400 3450
Wire Wire Line
7400 3450 7400 3550
Wire Wire Line
7400 3450 7750 3450
Wire Wire Line
7750 3450 7750 4250
Wire Wire Line
6900 3350 8000 3350
Wire Wire Line
8000 3350 8000 3550
Wire Wire Line
8000 3350 8250 3350
Connection ~ 8000 3350
Wire Wire Line
8450 3250 8450 3750
Wire Wire Line
6900 3250 8450 3250
$Comp
L Memory_EEPROM:24LC64 U3
U 1 1 61DDD4E7
P 6750 1550
F 0 "U3" H 6750 2031 50 0000 C CNN
F 1 "24LC64" H 6750 1940 50 0000 C CNN
F 2 "Package_SO:SOIC-8_3.9x4.9mm_P1.27mm" H 6750 1550 50 0001 C CNN
F 3 "http://ww1.microchip.com/downloads/en/DeviceDoc/21189f.pdf" H 6750 1550 50 0001 C CNN
1 6750 1550
1 0 0 -1
$EndComp
$Comp
L power:+3.3V #PWR0118
U 1 1 61DDE198
P 6750 1250
F 0 "#PWR0118" H 6750 1100 50 0001 C CNN
F 1 "+3.3V" H 6765 1423 50 0000 C CNN
F 2 "" H 6750 1250 50 0001 C CNN
F 3 "" H 6750 1250 50 0001 C CNN
1 6750 1250
1 0 0 -1
$EndComp
$Comp
L power:GND #PWR0119
U 1 1 61DDE4C0
P 6750 1850
F 0 "#PWR0119" H 6750 1600 50 0001 C CNN
F 1 "GND" H 6755 1677 50 0000 C CNN
F 2 "" H 6750 1850 50 0001 C CNN
F 3 "" H 6750 1850 50 0001 C CNN
1 6750 1850
1 0 0 -1
$EndComp
$Comp
L power:GND #PWR0120
U 1 1 61DDE71F
P 6300 1850
F 0 "#PWR0120" H 6300 1600 50 0001 C CNN
F 1 "GND" H 6305 1677 50 0000 C CNN
F 2 "" H 6300 1850 50 0001 C CNN
F 3 "" H 6300 1850 50 0001 C CNN
1 6300 1850
1 0 0 -1
$EndComp
Wire Wire Line
6350 1650 6300 1650
Wire Wire Line
6300 1650 6300 1850
Wire Wire Line
6350 1450 6300 1450
Wire Wire Line
6300 1450 6300 1550
Connection ~ 6300 1650
Wire Wire Line
6350 1550 6300 1550
Connection ~ 6300 1550
Wire Wire Line
6300 1550 6300 1650
Wire Wire Line
6900 2850 7400 2850
Wire Wire Line
6900 2950 7500 2950
Wire Wire Line
7150 1550 7400 1550
Wire Wire Line
7400 1550 7400 2850
Connection ~ 7400 2850
Wire Wire Line
7400 2850 7950 2850
Wire Wire Line
7500 1450 7500 2950
Wire Wire Line
7150 1450 7500 1450
Connection ~ 7500 2950
Wire Wire Line
7500 2950 8050 2950
$Comp
L power:GND #PWR0121
U 1 1 61DFBD18
P 7250 1850
F 0 "#PWR0121" H 7250 1600 50 0001 C CNN
F 1 "GND" H 7255 1677 50 0000 C CNN
F 2 "" H 7250 1850 50 0001 C CNN
F 3 "" H 7250 1850 50 0001 C CNN
1 7250 1850
1 0 0 -1
$EndComp
Wire Wire Line
7150 1650 7250 1650
Wire Wire Line
7250 1650 7250 1850
$Comp
L power:GND #PWR0122
U 1 1 61DEAC05
P 6000 1700
F 0 "#PWR0122" H 6000 1450 50 0001 C CNN
F 1 "GND" H 6005 1527 50 0000 C CNN
F 2 "" H 6000 1700 50 0001 C CNN
F 3 "" H 6000 1700 50 0001 C CNN
1 6000 1700
1 0 0 -1
$EndComp
$Comp
L Device:C C5
U 1 1 61DEA081
P 6000 1550
F 0 "C5" H 6115 1596 50 0000 L CNN
F 1 "10n" H 6115 1505 50 0000 L CNN
F 2 "Capacitor_SMD:C_0805_2012Metric_Pad1.18x1.45mm_HandSolder" H 6038 1400 50 0001 C CNN
F 3 "~" H 6000 1550 50 0001 C CNN
1 6000 1550
1 0 0 -1
$EndComp
$Comp
L power:+3.3V #PWR0123
U 1 1 61DEAFE2
P 6000 1400
F 0 "#PWR0123" H 6000 1250 50 0001 C CNN
F 1 "+3.3V" H 6015 1573 50 0000 C CNN
F 2 "" H 6000 1400 50 0001 C CNN
F 3 "" H 6000 1400 50 0001 C CNN
1 6000 1400
1 0 0 -1
$EndComp
$EndSCHEMATC

7
Software/.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
data/
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch
wifi_credentials.ini

10
Software/.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,10 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

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

@@ -0,0 +1,8 @@
import subprocess
revision = (
subprocess.check_output(["git", "rev-parse", "--short=10", "HEAD"])
.strip()
.decode("utf-8")
)
print("-DGIT_REV='\"%s\"'" % revision)

39
Software/include/README Normal file
View File

@@ -0,0 +1,39 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

46
Software/lib/README Normal file
View File

@@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

61
Software/platformio.ini Normal file
View File

@@ -0,0 +1,61 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[platformio]
extra_configs =
wifi_credentials.ini
[env:d1_mini]
platform = espressif8266
board = d1_mini
framework = arduino
upload_protocol = esptool
upload_speed = 921600
;upload_port = ChainLube_DDEFB2
;upload_protocol = espota
;upload_flags =
; --auth=${wifi_cred.admin_password}
build_flags =
!python git_rev_macro.py
-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.4m1m.ld
lib_ldf_mode = deep
lib_deps =
olikraus/U8g2 @ ^2.28.8
https://github.com/FastLED/FastLED.git#3d2ab78 ;fastled/FastLED @ ^3.5.0
sstaub/Ticker @ ^4.2.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.3
#define FLASH_FS_VERSION 1.3
#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;
}

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

@@ -0,0 +1,124 @@
#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"
#define EEPROM_SIZE_BYTES I2C_DEVICESIZE_24LC256
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", buff_timestamp, DTCStorage[i].Number, buff_active);
}
}
}

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;
}
}

14
Software/src/lubeapp.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef _LUBEAPP_H_
#define _LUBEAPP_H_
#include <Arduino.h>
#include <stdlib.h>
#include "config.h"
#include "common.h"
#include "globals.h"
#include "dtc.h"
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_

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

@@ -0,0 +1,572 @@
#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);
Serial.print("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);
Debug_ShowDTCs();
}
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

11
Software/test/README Normal file
View File

@@ -0,0 +1,11 @@
This directory is intended for PlatformIO Unit Testing and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PlatformIO Unit Testing:
- https://docs.platformio.org/page/plus/unit-testing.html

View File

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