@@ -114,7 +114,7 @@ void load_ecu_def(const ECUDef* ecu_progmem, ECUDef& ecu_ram)
114114}
115115
116116// Measurement value encoding formulas (VCDS/KWP1281 standard):
117- // 0x01: rpm = A * B * 0.2 (A=40 , B=rpm/8 )
117+ // 0x01: rpm = A * B * 0.2 (A=160 , B=rpm/32; max 8160 RPM )
118118// 0x02: % = A * B * 0.002 (placeholder for fuel trim; negative not representable)
119119// 0x04: - = A * |B-127| * 0.01 (K4; A=100 for fuel level: |B-127| = liters)
120120// OR A * B * 0.001 (raw form used for some sensors, e.g. lateral accel)
@@ -484,14 +484,34 @@ static const ECUDef ECU_TABLE[] PROGMEM = {
484484ECUDef current_ecu ; // loaded from PROGMEM on connect
485485uint8_t current_addr = 0x00 ; // decoded from 5-baud init
486486
487+ // VW 02J gearbox — Golf 4 1.6 16V, 195/65 R15 (circumference = 1.992 m)
488+ // km/h per RPM = (0.001992 * 60) / (gear_ratio * 4.238)
489+ static const float KMH_PER_RPM [6 ] = {
490+ 0.0f , // index 0 unused
491+ 0.007468f , // gear 1 (ratio 3.778)
492+ 0.013314f , // gear 2 (ratio 2.118)
493+ 0.020737f , // gear 3 (ratio 1.360)
494+ 0.029045f , // gear 4 (ratio 0.971)
495+ 0.037300f , // gear 5 (ratio 0.756)
496+ };
497+ // Upshift speed thresholds (km/h): index i = shift from gear i+1 → i+2
498+ static const float UPSHIFT_KMH [4 ] = {26.0f , 48.0f , 70.0f , 90.0f };
499+
500+ // Drive cycle: paired (time_s, speed_kmh) waypoints, linearly interpolated.
501+ // Narrative: idle → accel through gears → cruise at 120 → step-down decel → idle → repeat.
502+ static const uint8_t DRIVE_CYCLE_T [] = {0 , 3 , 8 , 12 , 18 , 24 , 28 , 34 , 40 , 46 , 52 , 56 , 62 ,
503+ 68 , 72 , 78 , 84 , 90 , 96 , 100 , 106 , 112 , 116 , 120 , 125 , 130 };
504+ static const uint8_t DRIVE_CYCLE_V [] = {0 , 0 , 30 , 30 , 50 , 50 , 35 , 35 , 60 , 60 , 90 , 90 , 70 ,
505+ 70 , 100 , 100 , 120 , 120 , 90 , 90 , 60 , 60 , 30 , 30 , 0 , 0 };
506+ #define DRIVE_CYCLE_POINTS 26
507+ static const uint32_t DRIVE_CYCLE_PERIOD_MS = 130000UL ;
508+
487509struct SimState
488510{
489511 unsigned long start_ms ;
490- unsigned long last_dist_ms ; // legacy, unused
491- float distance_km ; // accumulated travel distance since connect
492- float fuel_L ; // current fuel level, decreases with distance
493- unsigned long last_odo_update_ms ; // last odometer increment
494- unsigned long last_fuel_update_ms ; // last fuel decrement
512+ float distance_km ; // accumulated travel distance since connect
513+ float fuel_L ; // current fuel level, decreases with distance
514+ unsigned long last_update_ms ; // timestamp for dt-based odometer/fuel integration
495515} sim_state ;
496516
497517bool awake = false;
@@ -641,16 +661,40 @@ bool KWP_send_syncbytes()
641661float get_simulated_speed_kmh ()
642662{
643663 unsigned long elapsed_ms = millis () - sim_state .start_ms ;
664+ uint32_t t_ms = (uint32_t )(elapsed_ms % DRIVE_CYCLE_PERIOD_MS );
665+ uint16_t t_s_whole = (uint16_t )(t_ms / 1000UL );
666+ float frac = (float )(t_ms % 1000UL ) * 0.001f ;
644667
645- // Short idle: first 2s at 0 km/h
646- if (elapsed_ms < 2000UL )
647- return 0.0f ;
668+ uint8_t i = 0 ;
669+ for (uint8_t k = 0 ; k < DRIVE_CYCLE_POINTS - 1 ; k ++ )
670+ {
671+ if (DRIVE_CYCLE_T [k + 1 ] <= t_s_whole )
672+ i = k + 1 ;
673+ else
674+ break ;
675+ }
676+ uint8_t next = (i + 1 < DRIVE_CYCLE_POINTS ) ? i + 1 : i ;
677+ if (next == i )
678+ return (float )DRIVE_CYCLE_V [i ];
679+
680+ float dt_seg = (float )(DRIVE_CYCLE_T [next ] - DRIVE_CYCLE_T [i ]);
681+ float t_in = (float )(t_s_whole - DRIVE_CYCLE_T [i ]) + frac ;
682+ float alpha = t_in / dt_seg ;
683+ return (float )DRIVE_CYCLE_V [i ] +
684+ alpha * (float )((int )DRIVE_CYCLE_V [next ] - (int )DRIVE_CYCLE_V [i ]);
685+ }
648686
649- // Sine-wave 0-120 km/h, 30s period (ms resolution so polls see changes)
650- float phase = (float )(elapsed_ms % 30000UL ) / 30000.0f ; // 0..1
651- float angle = phase * 6.283185f ; // 2π
652- float speed = (sinf (angle ) + 1.0f ) * 60.0f ; // 0..120
653- return speed ;
687+ static uint8_t get_current_gear (float speed_kmh )
688+ {
689+ uint8_t gear = 1 ;
690+ for (uint8_t g = 0 ; g < 4 ; g ++ )
691+ {
692+ if (speed_kmh >= UPSHIFT_KMH [g ])
693+ gear = g + 2 ;
694+ else
695+ break ;
696+ }
697+ return gear ;
654698}
655699
656700int8_t get_simulated_coolant_temp ()
@@ -670,11 +714,15 @@ int8_t get_simulated_oil_temp()
670714uint16_t get_simulated_rpm ()
671715{
672716 float speed = get_simulated_speed_kmh ();
673- if (speed < 5.0f )
674- return 800 ; // idle
675- // RPM ~= (speed/30 + 0.8) * 1000, peaks ~4800 at 120 km/h
676- uint16_t rpm = (uint16_t )((speed / 30.0f + 0.8f ) * 1000.0f );
677- return rpm ;
717+ if (speed < 2.0f )
718+ return 800 ;
719+ uint8_t gear = get_current_gear (speed );
720+ float rpm_f = speed / KMH_PER_RPM [gear ];
721+ if (rpm_f < 800.0f )
722+ rpm_f = 800.0f ;
723+ if (rpm_f > 6500.0f )
724+ rpm_f = 6500.0f ;
725+ return (uint16_t )rpm_f ;
678726}
679727
680728bool KWP_send_group_reading (uint8_t group )
@@ -709,8 +757,8 @@ bool KWP_send_group_reading(uint8_t group)
709757 buf [4 ] = 100 ;
710758 buf [5 ] = (uint8_t )speed ; // km/h
711759 buf [6 ] = 0x01 ;
712- buf [7 ] = 40 ;
713- buf [8 ] = (uint8_t )(rpm / 8 ); // RPM
760+ buf [7 ] = 160 ;
761+ buf [8 ] = (uint8_t )(rpm / 32 ); // RPM
714762 // k=0x25 (37): F_B formula — stored value = b directly.
715763 // Normal: b=31. Fault sim: b=222 for 3 s every 15 s, starting after the first 15 s.
716764 buf [9 ] = 0x25 ;
@@ -726,28 +774,42 @@ bool KWP_send_group_reading(uint8_t group)
726774 }
727775 else if (current_addr == 0x17 && group == 2 )
728776 {
729- // Deterministic: +1 km every 3s (max 444444), -1L fuel every 10s (min 0)
730777 unsigned long now_ms = millis ();
731- // Odometer logic
732- if ((uint32_t )sim_state .distance_km < 444444 &&
733- (now_ms - sim_state .last_odo_update_ms ) >= 3000 )
778+ float dt_sec = (float )(now_ms - sim_state .last_update_ms ) * 0.001f ;
779+ if (dt_sec > 10.0f )
780+ dt_sec = 0.0f ; // guard: first call or stale state after reset
781+ sim_state .last_update_ms = now_ms ;
782+
783+ float speed = get_simulated_speed_kmh ();
784+
785+ // Odometer: integrate speed over time
786+ if (sim_state .distance_km < 444444.0f )
734787 {
735- sim_state .distance_km += 1 .0f ;
736- sim_state .last_odo_update_ms +=
737- 3000 * (( now_ms - sim_state .last_odo_update_ms ) / 3000 ); // catch up if polled late
788+ sim_state .distance_km += speed * dt_sec / 3600 .0f ;
789+ if ( sim_state .distance_km > 444444.0f )
790+ sim_state .distance_km = 444444.0f ;
738791 }
739- if ((uint32_t )sim_state .distance_km > 444444 )
740- sim_state .distance_km = 444444 ;
741792
742- // Fuel logic
743- if (( int ) sim_state .fuel_L > 0 && ( now_ms - sim_state . last_fuel_update_ms ) >= 10000 )
793+ // Fuel: physics-based consumption
794+ if (sim_state .fuel_L > 0.0f )
744795 {
745- sim_state .fuel_L -= 1.0f ;
746- sim_state .last_fuel_update_ms += 10000 * ((now_ms - sim_state .last_fuel_update_ms ) /
747- 10000 ); // catch up if polled late
796+ float fuel_rate ;
797+ if (speed < 2.0f )
798+ {
799+ fuel_rate = 0.6f / 3600.0f ; // idle ~0.6 L/h
800+ }
801+ else
802+ {
803+ uint16_t rpm = get_simulated_rpm ();
804+ // Base 7.0 L/100km + RPM load penalty + aerodynamic penalty above 100 km/h
805+ float l_per_100km = 7.0f + (float )(rpm - 800 ) * (1.5f / 5700.0f ) +
806+ (speed > 100.0f ? (speed - 100.0f ) * 0.015f : 0.0f );
807+ fuel_rate = (speed / 100.0f ) * l_per_100km / 3600.0f ;
808+ }
809+ sim_state .fuel_L -= fuel_rate * dt_sec ;
810+ if (sim_state .fuel_L < 0.0f )
811+ sim_state .fuel_L = 0.0f ;
748812 }
749- if (sim_state .fuel_L < 0.0f )
750- sim_state .fuel_L = 0.0f ;
751813
752814 // Odometer: type 0x24, formula: km = A*2560 + B*10. Max ~653350 km.
753815 uint32_t raw_km = 50000UL + (uint32_t )sim_state .distance_km ;
@@ -796,8 +858,8 @@ bool KWP_send_group_reading(uint8_t group)
796858 uint16_t rpm = get_simulated_rpm ();
797859
798860 buf [3 ] = 0x01 ;
799- buf [4 ] = 40 ;
800- buf [5 ] = (uint8_t )(rpm / 8 ); // RPM
861+ buf [4 ] = 160 ;
862+ buf [5 ] = (uint8_t )(rpm / 32 ); // RPM
801863 buf [6 ] = 0x05 ;
802864 buf [7 ] = 10 ;
803865 buf [8 ] = 117 ; // 17.0°C air temp (K5: 10*(117-100)*0.1 = 17°C)
@@ -814,8 +876,8 @@ bool KWP_send_group_reading(uint8_t group)
814876 uint16_t rpm = get_simulated_rpm ();
815877
816878 buf [3 ] = 0x01 ;
817- buf [4 ] = 40 ;
818- buf [5 ] = (uint8_t )(rpm / 8 ); // RPM
879+ buf [4 ] = 160 ;
880+ buf [5 ] = (uint8_t )(rpm / 32 ); // RPM
819881 buf [6 ] = 0x17 ;
820882 buf [7 ] = 100 ;
821883 buf [8 ] = 254 ; // 1016 mbar (100*254*0.04)
@@ -881,7 +943,7 @@ bool KWP_send_group_reading(uint8_t group)
881943 {
882944 // Engine ECU groups 30–125 (all [verify] from label file 036-906-034-APE)
883945 uint16_t rpm = get_simulated_rpm ();
884- uint8_t rpm_b = (uint8_t )(rpm / 8 );
946+ uint8_t rpm_b = (uint8_t )(rpm / 32 );
885947 int8_t coolant = get_simulated_coolant_temp ();
886948
887949 switch (group )
@@ -912,7 +974,7 @@ bool KWP_send_group_reading(uint8_t group)
912974 break ;
913975 case 34 : // O2 sensor aging test (B1-S1)
914976 buf [3 ] = 0x01 ;
915- buf [4 ] = 40 ;
977+ buf [4 ] = 160 ;
916978 buf [5 ] = rpm_b ;
917979 buf [6 ] = 0x21 ;
918980 buf [7 ] = 10 ;
@@ -960,7 +1022,7 @@ bool KWP_send_group_reading(uint8_t group)
9601022 break ;
9611023 case 46 : // Catalytic converter efficiency test
9621024 buf [3 ] = 0x01 ;
963- buf [4 ] = 40 ;
1025+ buf [4 ] = 160 ;
9641026 buf [5 ] = rpm_b ;
9651027 buf [6 ] = 0x21 ;
9661028 buf [7 ] = 10 ;
@@ -974,11 +1036,11 @@ bool KWP_send_group_reading(uint8_t group)
9741036 break ;
9751037 case 50 : // Speed regulation
9761038 buf [3 ] = 0x01 ;
977- buf [4 ] = 40 ;
1039+ buf [4 ] = 160 ;
9781040 buf [5 ] = rpm_b ;
9791041 buf [6 ] = 0x01 ;
980- buf [7 ] = 40 ;
981- buf [8 ] = 100 ; // target 800 RPM (40*100 *0.2)
1042+ buf [7 ] = 160 ;
1043+ buf [8 ] = 25 ; // target 800 RPM (160*25 *0.2)
9821044 buf [9 ] = 0x0E ;
9831045 buf [10 ] = 0 ;
9841046 buf [11 ] = 1 ; // A/C-Low (A/C-High/A/C-Low)
@@ -988,7 +1050,7 @@ bool KWP_send_group_reading(uint8_t group)
9881050 break ;
9891051 case 54 : // Throttle and pedal sensors
9901052 buf [3 ] = 0x01 ;
991- buf [4 ] = 40 ;
1053+ buf [4 ] = 160 ;
9921054 buf [5 ] = rpm_b ;
9931055 buf [6 ] = 0x0E ;
9941056 buf [7 ] = 0 ;
@@ -1002,7 +1064,7 @@ bool KWP_send_group_reading(uint8_t group)
10021064 break ;
10031065 case 55 : // Idle regulator
10041066 buf [3 ] = 0x01 ;
1005- buf [4 ] = 40 ;
1067+ buf [4 ] = 160 ;
10061068 buf [5 ] = rpm_b ;
10071069 buf [6 ] = 0x02 ;
10081070 buf [7 ] = 10 ;
@@ -1016,11 +1078,11 @@ bool KWP_send_group_reading(uint8_t group)
10161078 break ;
10171079 case 56 : // Idle torque regulation
10181080 buf [3 ] = 0x01 ;
1019- buf [4 ] = 40 ;
1081+ buf [4 ] = 160 ;
10201082 buf [5 ] = rpm_b ;
10211083 buf [6 ] = 0x01 ;
1022- buf [7 ] = 40 ;
1023- buf [8 ] = 100 ; // target 800 RPM
1084+ buf [7 ] = 160 ;
1085+ buf [8 ] = 25 ; // target 800 RPM (160*25*0.2)
10241086 buf [9 ] = 0x21 ;
10251087 buf [10 ] = 10 ;
10261088 buf [11 ] = 0 ; // idle regulator 0.0 Nm
@@ -1044,7 +1106,7 @@ bool KWP_send_group_reading(uint8_t group)
10441106 break ;
10451107 case 61 : // EPC system status
10461108 buf [3 ] = 0x01 ;
1047- buf [4 ] = 40 ;
1109+ buf [4 ] = 160 ;
10481110 buf [5 ] = rpm_b ;
10491111 buf [6 ] = 0x06 ;
10501112 buf [7 ] = 100 ;
@@ -1100,7 +1162,7 @@ bool KWP_send_group_reading(uint8_t group)
11001162 break ;
11011163 case 75 : // EGR test
11021164 buf [3 ] = 0x01 ;
1103- buf [4 ] = 40 ;
1165+ buf [4 ] = 160 ;
11041166 buf [5 ] = rpm_b ;
11051167 buf [6 ] = 0x17 ;
11061168 buf [7 ] = 100 ;
@@ -1114,7 +1176,7 @@ bool KWP_send_group_reading(uint8_t group)
11141176 break ;
11151177 case 99 : // OBD compatibility
11161178 buf [3 ] = 0x01 ;
1117- buf [4 ] = 40 ;
1179+ buf [4 ] = 160 ;
11181180 buf [5 ] = rpm_b ;
11191181 buf [6 ] = 0x05 ;
11201182 buf [7 ] = 10 ;
@@ -1146,27 +1208,27 @@ bool KWP_send_group_reading(uint8_t group)
11461208 }
11471209 case 120 : // Traction control (ASR/TCS)
11481210 buf [3 ] = 0x01 ;
1149- buf [4 ] = 40 ;
1211+ buf [4 ] = 160 ;
11501212 buf [5 ] = rpm_b ;
11511213 buf [6 ] = 0x01 ;
1152- buf [7 ] = 40 ;
1153- buf [8 ] = 100 ; // target 800 RPM
1214+ buf [7 ] = 160 ;
1215+ buf [8 ] = 25 ; // target 800 RPM (160*25*0.2)
11541216 buf [9 ] = 0x01 ;
1155- buf [10 ] = 40 ;
1217+ buf [10 ] = 160 ;
11561218 buf [11 ] = rpm_b ; // actual RPM
11571219 buf [12 ] = 0x0E ;
11581220 buf [13 ] = 0 ;
11591221 buf [14 ] = 1 ; // ASR not active
11601222 break ;
11611223 case 122 : // Transmission torque reduction
11621224 buf [3 ] = 0x01 ;
1163- buf [4 ] = 40 ;
1225+ buf [4 ] = 160 ;
11641226 buf [5 ] = rpm_b ;
11651227 buf [6 ] = 0x01 ;
1166- buf [7 ] = 40 ;
1167- buf [8 ] = 100 ; // target 800 RPM
1228+ buf [7 ] = 160 ;
1229+ buf [8 ] = 25 ; // target 800 RPM (160*25*0.2)
11681230 buf [9 ] = 0x01 ;
1169- buf [10 ] = 40 ;
1231+ buf [10 ] = 160 ;
11701232 buf [11 ] = rpm_b ; // actual RPM
11711233 buf [12 ] = 0x0E ;
11721234 buf [13 ] = 0 ;
@@ -1739,11 +1801,9 @@ bool connect()
17391801 // Initialize simulation state
17401802 unsigned long now = millis ();
17411803 sim_state .start_ms = now ;
1742- sim_state .last_dist_ms = now ; // legacy, unused
17431804 sim_state .distance_km = 0.0f ;
17441805 sim_state .fuel_L = 55.0f ; // full tank
1745- sim_state .last_odo_update_ms = now ;
1746- sim_state .last_fuel_update_ms = now ;
1806+ sim_state .last_update_ms = now ;
17471807
17481808 g .setColor (TFT_YELLOW );
17491809 g .print ("connected" , RIGHT , rows [7 ]);
0 commit comments