5 Commits

13 changed files with 1251 additions and 257 deletions

View File

@ -6,7 +6,7 @@
(paper "A4")
(title_block
(title "DenshaBekutoru 電車ベクトル (Train Vector)")
(date "2025-11-02")
(date "2025-11-10")
(rev "#001")
(company "ToGo-Lab")
(comment 1 "- https://togo-lab.io/")
@ -2299,12 +2299,6 @@
(color 0 0 0 0)
(uuid "18d14335-de9f-45f2-a4cc-180a2d8dc852")
)
(junction
(at 182.88 106.68)
(diameter 0)
(color 0 0 0 0)
(uuid "1f239e7b-1e06-4b14-af20-034b4b4ffd89")
)
(junction
(at 53.34 82.55)
(diameter 0)
@ -2324,10 +2318,10 @@
(uuid "30b28ed0-5697-4199-8822-158e2b1a60c8")
)
(junction
(at 182.88 101.6)
(at 208.28 111.76)
(diameter 0)
(color 0 0 0 0)
(uuid "3801c894-ae42-4eda-8d02-a60c9af73401")
(uuid "3946b4d0-1318-4fdb-84c2-f810f95c281b")
)
(junction
(at 63.5 49.53)
@ -2347,12 +2341,6 @@
(color 0 0 0 0)
(uuid "62c162a4-02aa-4d73-a877-dd43b712ff83")
)
(junction
(at 233.68 104.14)
(diameter 0)
(color 0 0 0 0)
(uuid "69403b42-500c-4703-8b4b-71225e9752fd")
)
(junction
(at 254 49.53)
(diameter 0)
@ -2371,24 +2359,30 @@
(color 0 0 0 0)
(uuid "755fdb91-31e6-4c85-9a85-356faed56e78")
)
(junction
(at 182.88 113.03)
(diameter 0)
(color 0 0 0 0)
(uuid "7584aefd-e768-4fad-9ef8-29c598224de3")
)
(junction
(at 165.1 77.47)
(diameter 0)
(color 0 0 0 0)
(uuid "7661277d-70d8-4690-ba7d-cd49253bbdbb")
)
(junction
(at 208.28 111.76)
(diameter 0)
(color 0 0 0 0)
(uuid "7720ca20-9bd5-4382-86b1-332ffca59368")
)
(junction
(at 127 58.42)
(diameter 0)
(color 0 0 0 0)
(uuid "82ef0e26-c162-4d21-a930-310df31322ae")
)
(junction
(at 214.63 142.24)
(diameter 0)
(color 0 0 0 0)
(uuid "88077938-aa1d-4441-a812-b1bea64d7459")
)
(junction
(at 76.2 58.42)
(diameter 0)
@ -2396,10 +2390,16 @@
(uuid "93826f1f-32d3-46b1-8387-53e4a96a8d03")
)
(junction
(at 208.28 99.06)
(at 233.68 110.49)
(diameter 0)
(color 0 0 0 0)
(uuid "9d05bd90-8bfe-4b81-b321-b8273bc65092")
(uuid "9a844833-2e1e-4ee4-b470-2fb0d234712e")
)
(junction
(at 227.33 139.7)
(diameter 0)
(color 0 0 0 0)
(uuid "9f28060d-90ac-4c8f-9507-b3471e0ddcd1")
)
(junction
(at 114.3 49.53)
@ -2438,10 +2438,10 @@
(uuid "ce2f90af-1659-40af-b731-f02d61bb1e3a")
)
(junction
(at 233.68 109.22)
(at 240.03 140.97)
(diameter 0)
(color 0 0 0 0)
(uuid "e579b160-1f59-44ec-a035-ed2c06c25011")
(uuid "dc5976c8-65a3-4be8-90d6-fe61922b0432")
)
(junction
(at 222.25 92.71)
@ -2483,16 +2483,6 @@
)
(uuid "03871aea-97d7-4df5-a644-61febfe36353")
)
(wire
(pts
(xy 208.28 69.85) (xy 208.28 99.06)
)
(stroke
(width 0)
(type default)
)
(uuid "03f9927e-2206-4a73-bbea-f6e782a37f7b")
)
(wire
(pts
(xy 76.2 55.88) (xy 76.2 58.42)
@ -2513,16 +2503,6 @@
)
(uuid "077acef7-8d2f-4939-8819-d10bbdfd6345")
)
(wire
(pts
(xy 208.28 111.76) (xy 250.19 111.76)
)
(stroke
(width 0)
(type default)
)
(uuid "082f72d9-2fc8-4469-94c6-6cb9280f9977")
)
(wire
(pts
(xy 50.8 49.53) (xy 63.5 49.53)
@ -2535,23 +2515,23 @@
)
(wire
(pts
(xy 233.68 109.22) (xy 252.73 109.22)
(xy 189.23 137.16) (xy 189.23 142.24)
)
(stroke
(width 0)
(type default)
)
(uuid "0c898650-b37b-4cae-a9d8-27195bbb4396")
(uuid "0c13ccac-cce8-42a0-820d-d03a8790adb7")
)
(wire
(pts
(xy 208.28 99.06) (xy 208.28 111.76)
(xy 254 113.03) (xy 254 139.7)
)
(stroke
(width 0)
(type default)
)
(uuid "0ca3aab4-c892-49b3-b42a-01f0ffe5d91e")
(uuid "0d57923f-8ff5-40b0-9a64-e0ca56564ab2")
)
(wire
(pts
@ -2573,6 +2553,16 @@
)
(uuid "127e5934-d799-4acc-8376-348ea723e5b5")
)
(wire
(pts
(xy 255.27 111.76) (xy 255.27 140.97)
)
(stroke
(width 0)
(type default)
)
(uuid "162b7e51-8fc5-4fc9-800a-3dbb1710f5f3")
)
(wire
(pts
(xy 238.76 67.31) (xy 182.88 67.31)
@ -2605,33 +2595,23 @@
)
(wire
(pts
(xy 257.81 147.32) (xy 257.81 104.14)
(xy 208.28 111.76) (xy 208.28 114.3)
)
(stroke
(width 0)
(type default)
)
(uuid "1d364f04-23dd-4336-937c-2d6cb4cd5b37")
(uuid "1e594437-c747-4142-bf93-d5c60d13cb81")
)
(wire
(pts
(xy 240.03 152.4) (xy 264.16 152.4)
(xy 240.03 140.97) (xy 176.53 140.97)
)
(stroke
(width 0)
(type default)
)
(uuid "225616c9-9869-456f-b62e-298c58f38ac5")
)
(wire
(pts
(xy 252.73 139.7) (xy 252.73 109.22)
)
(stroke
(width 0)
(type default)
)
(uuid "231bec29-1fac-4e77-b372-a999d1d60763")
(uuid "21b7c7bb-11d7-4208-a894-1ed9b6dc71fe")
)
(wire
(pts
@ -2645,7 +2625,7 @@
)
(wire
(pts
(xy 233.68 104.14) (xy 233.68 109.22)
(xy 233.68 72.39) (xy 233.68 110.49)
)
(stroke
(width 0)
@ -2685,13 +2665,13 @@
)
(wire
(pts
(xy 233.68 72.39) (xy 233.68 104.14)
(xy 233.68 110.49) (xy 233.68 114.3)
)
(stroke
(width 0)
(type default)
)
(uuid "2f2c9529-9c06-4377-a6eb-d622abac99bd")
(uuid "34512f54-666e-4e4a-8cda-739c8897a60c")
)
(wire
(pts
@ -2743,16 +2723,6 @@
)
(uuid "3b785ac1-70c9-42d9-91b0-d1b7a300d3a4")
)
(wire
(pts
(xy 264.16 152.4) (xy 264.16 99.06)
)
(stroke
(width 0)
(type default)
)
(uuid "3b7c71ab-0d10-48fe-873f-2e76fa938cf6")
)
(wire
(pts
(xy 165.1 77.47) (xy 238.76 77.47)
@ -2813,6 +2783,16 @@
)
(uuid "478627e1-22f0-4aa5-8f76-3cf20da43807")
)
(wire
(pts
(xy 201.93 137.16) (xy 201.93 139.7)
)
(stroke
(width 0)
(type default)
)
(uuid "49f1c1df-364e-4786-90aa-5c8ab3f3d486")
)
(wire
(pts
(xy 222.25 92.71) (xy 254 92.71)
@ -2833,6 +2813,16 @@
)
(uuid "4d6622c3-b033-45ec-be29-398c0802ecf2")
)
(wire
(pts
(xy 208.28 111.76) (xy 255.27 111.76)
)
(stroke
(width 0)
(type default)
)
(uuid "4ea12a6b-db60-41f9-8806-fb74763d3b64")
)
(wire
(pts
(xy 114.3 46.99) (xy 114.3 49.53)
@ -2845,7 +2835,7 @@
)
(wire
(pts
(xy 182.88 67.31) (xy 182.88 101.6)
(xy 182.88 67.31) (xy 182.88 113.03)
)
(stroke
(width 0)
@ -2873,16 +2863,6 @@
)
(uuid "5190df3c-09f0-4ac2-86fd-fdd45881d197")
)
(wire
(pts
(xy 233.68 104.14) (xy 257.81 104.14)
)
(stroke
(width 0)
(type default)
)
(uuid "52439f80-4fb4-4838-ada4-cbfeaeee8fc4")
)
(wire
(pts
(xy 127 46.99) (xy 127 49.53)
@ -2913,16 +2893,6 @@
)
(uuid "53fc23ba-ea79-488d-9c39-9ce730bd5817")
)
(wire
(pts
(xy 176.53 142.24) (xy 250.19 142.24)
)
(stroke
(width 0)
(type default)
)
(uuid "548f29cf-4665-44c0-84b7-fa4b211ceb6b")
)
(wire
(pts
(xy 189.23 123.19) (xy 189.23 129.54)
@ -2943,16 +2913,6 @@
)
(uuid "5adcb769-63e8-4dc6-9022-8c3dced2fc1a")
)
(wire
(pts
(xy 201.93 137.16) (xy 201.93 144.78)
)
(stroke
(width 0)
(type default)
)
(uuid "5b378fd8-d1f7-4c32-900b-24f6a5bcafce")
)
(wire
(pts
(xy 69.85 91.44) (xy 69.85 92.71)
@ -3063,16 +3023,6 @@
)
(uuid "695bb26e-eca8-4e2b-beed-eb2eeaa25e7e")
)
(wire
(pts
(xy 264.16 99.06) (xy 208.28 99.06)
)
(stroke
(width 0)
(type default)
)
(uuid "69d37a24-2f55-4eb8-9314-4776f03c3ffe")
)
(wire
(pts
(xy 254 45.72) (xy 254 49.53)
@ -3085,13 +3035,13 @@
)
(wire
(pts
(xy 201.93 144.78) (xy 255.27 144.78)
(xy 182.88 113.03) (xy 182.88 114.3)
)
(stroke
(width 0)
(type default)
)
(uuid "727d6958-57dc-4380-b73a-5026418021ce")
(uuid "729f75d0-a040-43ca-a20a-ad6a7746aa99")
)
(wire
(pts
@ -3113,26 +3063,6 @@
)
(uuid "7ab0e3a2-d291-4644-91a2-310e3f3779b8")
)
(wire
(pts
(xy 250.19 142.24) (xy 250.19 111.76)
)
(stroke
(width 0)
(type default)
)
(uuid "7c3beac1-0ae4-4e05-91f3-88718ad266e5")
)
(wire
(pts
(xy 227.33 149.86) (xy 261.62 149.86)
)
(stroke
(width 0)
(type default)
)
(uuid "7fb6d153-f9f5-443a-a000-36bca9d349f1")
)
(wire
(pts
(xy 50.8 102.87) (xy 50.8 104.14)
@ -3155,13 +3085,23 @@
)
(wire
(pts
(xy 176.53 137.16) (xy 176.53 142.24)
(xy 256.54 110.49) (xy 256.54 142.24)
)
(stroke
(width 0)
(type default)
)
(uuid "84f2bdbb-ad23-4a30-9f6b-147501d2e6e1")
(uuid "893bba9d-6d8d-439b-a8ee-0f8e4d85ae3b")
)
(wire
(pts
(xy 227.33 137.16) (xy 227.33 139.7)
)
(stroke
(width 0)
(type default)
)
(uuid "8a6c82dd-5ee1-4bac-a9b2-6fd9cf6f5643")
)
(wire
(pts
@ -3183,16 +3123,6 @@
)
(uuid "8f36358c-8110-4d1a-8d48-c37408cf3aef")
)
(wire
(pts
(xy 233.68 109.22) (xy 233.68 114.3)
)
(stroke
(width 0)
(type default)
)
(uuid "913bc1ed-25d5-4ea2-b600-6bc559eb3168")
)
(wire
(pts
(xy 208.28 123.19) (xy 214.63 123.19)
@ -3223,6 +3153,16 @@
)
(uuid "92b04ca6-d759-43f8-bbdc-5638b2d51062")
)
(wire
(pts
(xy 255.27 140.97) (xy 240.03 140.97)
)
(stroke
(width 0)
(type default)
)
(uuid "9447f0b4-0b0d-4fe5-9775-1809a23b5005")
)
(wire
(pts
(xy 158.75 49.53) (xy 165.1 49.53)
@ -3245,33 +3185,13 @@
)
(wire
(pts
(xy 182.88 101.6) (xy 182.88 106.68)
(xy 233.68 110.49) (xy 256.54 110.49)
)
(stroke
(width 0)
(type default)
)
(uuid "982763b3-f7f1-4b51-8a97-cd1857910902")
)
(wire
(pts
(xy 214.63 147.32) (xy 257.81 147.32)
)
(stroke
(width 0)
(type default)
)
(uuid "99b58e1e-63fe-4385-9ad3-78377caa2c6b")
)
(wire
(pts
(xy 189.23 139.7) (xy 252.73 139.7)
)
(stroke
(width 0)
(type default)
)
(uuid "9bb930c1-35a8-492d-8864-c4cec609cfe6")
(uuid "9a300a0e-b692-474e-9f90-3fa69a7adbdb")
)
(wire
(pts
@ -3303,6 +3223,16 @@
)
(uuid "a0bc0158-aef3-48a6-97f3-b4245e2346f1")
)
(wire
(pts
(xy 182.88 113.03) (xy 254 113.03)
)
(stroke
(width 0)
(type default)
)
(uuid "a103d7d7-dd85-439b-b914-5436d9cb9aa1")
)
(wire
(pts
(xy 54.61 85.09) (xy 55.88 85.09)
@ -3323,6 +3253,16 @@
)
(uuid "a3bc624e-8b24-475a-9ae9-f857eac9aa4d")
)
(wire
(pts
(xy 214.63 137.16) (xy 214.63 142.24)
)
(stroke
(width 0)
(type default)
)
(uuid "a4caf5f6-0faa-452d-8714-391f8ed973cf")
)
(wire
(pts
(xy 165.1 68.58) (xy 165.1 77.47)
@ -3345,23 +3285,13 @@
)
(wire
(pts
(xy 227.33 137.16) (xy 227.33 149.86)
(xy 214.63 142.24) (xy 189.23 142.24)
)
(stroke
(width 0)
(type default)
)
(uuid "ac37cdac-25ad-434a-a1a1-c78237111ca4")
)
(wire
(pts
(xy 240.03 137.16) (xy 240.03 152.4)
)
(stroke
(width 0)
(type default)
)
(uuid "adaa6318-4f12-489e-b59e-ba90c8a1554d")
(uuid "af6d0fe2-87fc-480d-a7cc-6cde78fc3fb1")
)
(wire
(pts
@ -3373,16 +3303,6 @@
)
(uuid "b1374777-4398-4187-b6a0-9465281b84b4")
)
(wire
(pts
(xy 261.62 149.86) (xy 261.62 101.6)
)
(stroke
(width 0)
(type default)
)
(uuid "b40b58ff-6914-4e05-a619-c465b4ba2209")
)
(wire
(pts
(xy 182.88 123.19) (xy 176.53 123.19)
@ -3393,16 +3313,6 @@
)
(uuid "b76cabdd-3a68-4a5e-8430-138e00a5350b")
)
(wire
(pts
(xy 214.63 137.16) (xy 214.63 147.32)
)
(stroke
(width 0)
(type default)
)
(uuid "b7ec96c4-d805-462d-99db-3a3020463c33")
)
(wire
(pts
(xy 53.34 73.66) (xy 55.88 73.66)
@ -3413,6 +3323,16 @@
)
(uuid "bc7b09cc-b7d0-4e90-b681-6467ec5be6f6")
)
(wire
(pts
(xy 176.53 137.16) (xy 176.53 140.97)
)
(stroke
(width 0)
(type default)
)
(uuid "bf13604a-6704-43bc-846d-a2bbdc2d5aab")
)
(wire
(pts
(xy 63.5 58.42) (xy 76.2 58.42)
@ -3433,16 +3353,6 @@
)
(uuid "c2082274-0c44-4e62-8322-b503953a2385")
)
(wire
(pts
(xy 182.88 106.68) (xy 182.88 114.3)
)
(stroke
(width 0)
(type default)
)
(uuid "c35f91ae-df6b-4d61-8c0b-b70966a657e2")
)
(wire
(pts
(xy 238.76 69.85) (xy 208.28 69.85)
@ -3453,16 +3363,6 @@
)
(uuid "c63d628a-8795-4c40-8732-347a0b08719f")
)
(wire
(pts
(xy 189.23 137.16) (xy 189.23 139.7)
)
(stroke
(width 0)
(type default)
)
(uuid "c8927b09-9613-4eac-a0a8-e7b049965fde")
)
(wire
(pts
(xy 152.4 74.93) (xy 238.76 74.93)
@ -3475,7 +3375,7 @@
)
(wire
(pts
(xy 208.28 111.76) (xy 208.28 114.3)
(xy 208.28 69.85) (xy 208.28 111.76)
)
(stroke
(width 0)
@ -3503,6 +3403,16 @@
)
(uuid "cc015d09-efe0-4b4a-add3-2e0b8cc3cd77")
)
(wire
(pts
(xy 240.03 137.16) (xy 240.03 140.97)
)
(stroke
(width 0)
(type default)
)
(uuid "cc2d6793-b280-422c-b80d-02d4a29cfae9")
)
(wire
(pts
(xy 152.4 58.42) (xy 152.4 60.96)
@ -3523,16 +3433,6 @@
)
(uuid "cdae4557-6272-4c5d-bf2f-f8b07dcc8dae")
)
(wire
(pts
(xy 182.88 101.6) (xy 261.62 101.6)
)
(stroke
(width 0)
(type default)
)
(uuid "cf1700ae-fd5a-4ab4-9f23-6ee0a5c8a3ce")
)
(wire
(pts
(xy 233.68 121.92) (xy 233.68 123.19)
@ -3613,6 +3513,16 @@
)
(uuid "ed2d1d23-9c73-4f7a-8b96-87e2c7f148da")
)
(wire
(pts
(xy 227.33 139.7) (xy 201.93 139.7)
)
(stroke
(width 0)
(type default)
)
(uuid "ede25ee6-a01c-4891-9a5e-b55925e1a54b")
)
(wire
(pts
(xy 227.33 123.19) (xy 227.33 129.54)
@ -3633,6 +3543,16 @@
)
(uuid "ef9f7809-5e6b-45c8-9644-679f84743bb9")
)
(wire
(pts
(xy 256.54 142.24) (xy 214.63 142.24)
)
(stroke
(width 0)
(type default)
)
(uuid "f01911e7-a9d8-4ec0-8343-a969bb4a16f3")
)
(wire
(pts
(xy 127 49.53) (xy 127 50.8)
@ -3653,16 +3573,6 @@
)
(uuid "f581af55-ffa5-4c77-b299-395ef0284bd8")
)
(wire
(pts
(xy 255.27 144.78) (xy 255.27 106.68)
)
(stroke
(width 0)
(type default)
)
(uuid "f5ab323b-6e45-4ffd-9d9e-29aabec80515")
)
(wire
(pts
(xy 69.85 80.01) (xy 69.85 81.28)
@ -3703,16 +3613,6 @@
)
(uuid "f8ccf3eb-3caf-45a2-a97e-16395719fc9f")
)
(wire
(pts
(xy 182.88 106.68) (xy 255.27 106.68)
)
(stroke
(width 0)
(type default)
)
(uuid "f8e744d1-4573-4a01-bb7d-ec640cd63dd4")
)
(wire
(pts
(xy 127 58.42) (xy 127 60.96)
@ -3733,9 +3633,19 @@
)
(uuid "fac1ca40-1662-4c8b-ab11-b61ce411fd2b")
)
(wire
(pts
(xy 254 139.7) (xy 227.33 139.7)
)
(stroke
(width 0)
(type default)
)
(uuid "fdca9757-291a-4a60-bd80-a3d5b956a35f")
)
(text "Idea:\nPWM and Charlieplexing for max. flexibility\nLater Layout: for each LED one Connector."
(exclude_from_sim no)
(at 177.038 151.384 0)
(at 177.038 149.352 0)
(effects
(font
(size 1.27 1.27)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

View File

@ -0,0 +1,217 @@
/*
0004_DenshaBekutoru Optocoupler Averaging Test
Target dev board (later): Arduino Pro Mini 5V / 16 MHz (ATmega328P)
Measures average voltage on:
- A2 -> later ATtiny85 PB3 (XTAL1, Pin 2)
- A3 -> later ATtiny85 PB4 (XTAL2, Pin 3)
Boot calibration:
- assumes "no movement" at startup
- measures V1/V2 AverageRepeat times
- computes di = |V1 - V2| each time
- VdiffBaseline = median(di)
- VdiffThreshold = max(VdiffBaseline * VdiffThresholdMargin, VdiffThresholdMinVolts)
*/
const unsigned long SAMPLE_WINDOW_MS = 100; // averaging window per measurement
const unsigned long SAMPLE_DELAY_US = 500; // delay between ADC samples (~2 kHz)
const uint8_t ADC_PIN_1 = A2; // Pro Mini A2 -> later ATtiny85 PB3 (XTAL1, Pin 2)
const uint8_t ADC_PIN_2 = A3; // Pro Mini A3 -> later ATtiny85 PB4 (XTAL2, Pin 3)
// LEDs for direction indication (avoid D0/D1)
const uint8_t LED_DIR1_PIN = 2; // D2
const uint8_t LED_DIR2_PIN = 3; // D3
// Serial output control (0 = no output, 1 = output)
bool SerialOutputAllow = true;
// ---- Calibration parameters ----
const uint16_t AverageRepeat = 100; // number of init measurements
const float VdiffThresholdMargin = 1.50f; // multiplier (e.g., 1.5 = +50% margin)
const float VdiffThresholdMinVolts = 0.03f; // floor in volts (optional but practical)
// Globals: latest measured averaged voltages
float v1 = 0.0f;
float v2 = 0.0f;
// Calibration globals
float VdiffBaseline = 0.0f; // median(|V1-V2|) measured at boot (no movement)
float VdiffThreshold = 0.0f; // final threshold used for direction decision
// Direction encoding requirement:
// 0 = no direction
// 2 = direction 1 (maps to LED pin D2)
// 3 = direction 2 (maps to LED pin D3)
byte direction = 0;
static inline float absf(float x) { return (x >= 0.0f) ? x : -x; }
static inline float maxf(float a, float b) { return (a > b) ? a : b; }
static void applyDirectionOutputs()
{
digitalWrite(LED_DIR1_PIN, LOW);
digitalWrite(LED_DIR2_PIN, LOW);
if (direction == LED_DIR1_PIN) {
digitalWrite(LED_DIR1_PIN, HIGH);
} else if (direction == LED_DIR2_PIN) {
digitalWrite(LED_DIR2_PIN, HIGH);
}
}
// Measure averaged voltages (updates globals v1/v2)
static void measureAveragedVoltages()
{
unsigned long startTime = millis();
unsigned long sum1 = 0;
unsigned long sum2 = 0;
unsigned long samples = 0;
while (millis() - startTime < SAMPLE_WINDOW_MS) {
sum1 += analogRead(ADC_PIN_1);
sum2 += analogRead(ADC_PIN_2);
samples++;
delayMicroseconds(SAMPLE_DELAY_US);
}
float avg1 = (float)sum1 / samples;
float avg2 = (float)sum2 / samples;
// Convert ADC value to voltage (default analog reference = Vcc ~ 5V)
v1 = avg1 * (5.0f / 1023.0f);
v2 = avg2 * (5.0f / 1023.0f);
}
static void sortFloatArray(float *a, uint16_t n)
{
// Insertion sort (n=100 is small, OK)
for (uint16_t i = 1; i < n; i++) {
float key = a[i];
int16_t j = (int16_t)i - 1;
while (j >= 0 && a[j] > key) {
a[j + 1] = a[j];
j--;
}
a[j + 1] = key;
}
}
static float medianOfSortedFloatArray(const float *a, uint16_t n)
{
if (n == 0) return 0.0f;
if (n & 1) {
return a[n / 2];
} else {
return (a[(n / 2) - 1] + a[n / 2]) * 0.5f;
}
}
// Calibrate baseline + threshold once at boot/reset
// Parameter must be only AverageRepeat (per your requirement).
static void calibrateVdiffThreshold(uint16_t averageRepeat)
{
if (averageRepeat < 1) averageRepeat = 1;
if (averageRepeat > 200) averageRepeat = 200; // RAM safety cap
float diffs[200];
for (uint16_t i = 0; i < averageRepeat; i++) {
measureAveragedVoltages();
diffs[i] = absf(v1 - v2); // baseline mismatch sample
}
sortFloatArray(diffs, averageRepeat);
VdiffBaseline = medianOfSortedFloatArray(diffs, averageRepeat);
// Final threshold with margin + floor
VdiffThreshold = maxf(VdiffBaseline * VdiffThresholdMargin, VdiffThresholdMinVolts);
}
static void updateDirectionFromVoltages()
{
float diff = v1 - v2;
float absDiff = absf(diff);
if (absDiff <= VdiffThreshold) {
direction = 0;
} else {
// Mapping choice:
// v1 lower than v2 -> direction 1 (D2)
// v2 lower than v1 -> direction 2 (D3)
direction = (diff < 0.0f) ? LED_DIR1_PIN : LED_DIR2_PIN;
}
}
static void serialPrintAll()
{
if (!SerialOutputAllow) return;
Serial.print("A2 for PB3 XTAL1, Pin2: ");
Serial.print(v1, 3);
Serial.print(" V | ");
Serial.print("A3 for PB4 XTAL2, Pin3: ");
Serial.print(v2, 3);
Serial.print(" V | ");
Serial.print("|V1-V2|: ");
Serial.print(absf(v1 - v2), 3);
Serial.print(" V | ");
Serial.print("VdiffBaseline: ");
Serial.print(VdiffBaseline, 3);
Serial.print(" V | ");
Serial.print("VdiffThresholdMargin: ");
Serial.print(VdiffThresholdMargin, 2);
Serial.print(" x | ");
Serial.print("VdiffThresholdMinVolts: ");
Serial.print(VdiffThresholdMinVolts, 3);
Serial.print(" V | ");
Serial.print("VdiffThreshold: ");
Serial.print(VdiffThreshold, 3);
Serial.print(" V | ");
Serial.print("direction: ");
Serial.println(direction);
}
void setup() {
Serial.begin(115200);
pinMode(ADC_PIN_1, INPUT);
pinMode(ADC_PIN_2, INPUT);
pinMode(LED_DIR1_PIN, OUTPUT);
pinMode(LED_DIR2_PIN, OUTPUT);
calibrateVdiffThreshold(AverageRepeat);
// One measurement for initial output + outputs
measureAveragedVoltages();
updateDirectionFromVoltages();
applyDirectionOutputs();
if (SerialOutputAllow) {
Serial.println("=== DenshaBekutoru Optocoupler Average Test (Arduino Pro Mini 5V/16MHz) ===");
Serial.print("AverageRepeat: ");
Serial.println(AverageRepeat);
}
serialPrintAll();
}
void loop() {
measureAveragedVoltages();
updateDirectionFromVoltages();
applyDirectionOutputs();
serialPrintAll();
delay(300);
}

View File

@ -0,0 +1,58 @@
/*
0004_DenshaBekutoru ? Optocoupler Averaging Test (UNO)
Measures average voltage on:
- A2 -> later ATtiny85 PB3 (XTAL1, Pin 2)
- A3 -> later ATtiny85 PB4 (XTAL2, Pin 3)
Integration window is configurable via SAMPLE_WINDOW_MS.
by tgohle, last edit 20260111
*/
const unsigned long SAMPLE_WINDOW_MS = 100; // change here
const unsigned long SAMPLE_DELAY_US = 500; // delay between ADC samples
const uint8_t ADC_PIN_1 = A2; // UNO A2 -> later ATtiny85 PB3 (XTAL1, Pin 2)
const uint8_t ADC_PIN_2 = A3; // UNO A3 -> later ATtiny85 PB4 (XTAL2, Pin 3)
void setup() {
Serial.begin(115200);
pinMode(ADC_PIN_1, INPUT);
pinMode(ADC_PIN_2, INPUT);
Serial.println("=== DenshaBekutoru Optocoupler Average Test ===");
}
void loop() {
unsigned long startTime = millis();
unsigned long sum1 = 0;
unsigned long sum2 = 0;
unsigned long samples = 0;
while (millis() - startTime < SAMPLE_WINDOW_MS) {
sum1 += analogRead(ADC_PIN_1);
sum2 += analogRead(ADC_PIN_2);
samples++;
delayMicroseconds(SAMPLE_DELAY_US);
}
float avg1 = (float)sum1 / samples;
float avg2 = (float)sum2 / samples;
float v1 = avg1 * (5.0 / 1023.0);
float v2 = avg2 * (5.0 / 1023.0);
Serial.print("A2 for PB3 XTAL1, Pin2: ");
Serial.print(v1, 3);
Serial.print(" V | ");
Serial.print("A3 for PB4 XTAL2, Pin3: ");
Serial.print(v2, 3);
Serial.println(" V");
delay(300);
}

View File

@ -0,0 +1,292 @@
/*
0004_DenshaBekutoru Version 0.1 Test
Target dev board (later): Arduino Pro Mini 5V / 16 MHz (ATmega328P)
----------------------------------------------------------------------------
State machine (direction memory)
----------------------------------------------------------------------------
Inputs derived from v1, v2, VdiffThreshold:
N (Neutral): |v1 - v2| <= VdiffThreshold
D1 (v1>>v2): (v1 - v2) > VdiffThreshold
D2 (v2>>v1): (v2 - v1) > VdiffThreshold
State encoding:
0 = NONE
2 = DIR1
3 = DIR2
Transition table:
Current \ Input | N (neutral) | D1 (v1>>v2) | D2 (v2>>v1)
-----------------------------------------------------------------
0 (NONE) | 0 | 2 | 3
2 (DIR1) | 2 | 2 | 3
3 (DIR2) | 3 | 2 | 3
Mermaid (documentation)
-----------------------
stateDiagram-v2
[*] --> NONE
state "0 : NONE" as NONE
state "2 : DIR1 (v1>>v2)" as DIR1
state "3 : DIR2 (v2>>v1)" as DIR2
NONE --> NONE : |v1 - v2| <= T
NONE --> DIR1 : v1 - v2 > T
NONE --> DIR2 : v2 - v1 > T
DIR1 --> DIR1 : |v1 - v2| <= T
DIR1 --> DIR1 : v1 - v2 > T
DIR1 --> DIR2 : v2 - v1 > T
DIR2 --> DIR2 : |v1 - v2| <= T
DIR2 --> DIR2 : v2 - v1 > T
DIR2 --> DIR1 : v1 - v2 > T
*/
const unsigned long SAMPLE_WINDOW_MS = 100; // averaging window per measurement
const unsigned long SAMPLE_DELAY_US = 500; // delay between ADC samples (~2 kHz)
const uint8_t ADC_PIN_1 = A2; // Pro Mini A2 -> later ATtiny85 PB3 (XTAL1, Pin 2)
const uint8_t ADC_PIN_2 = A3; // Pro Mini A3 -> later ATtiny85 PB4 (XTAL2, Pin 3)
// Direction LEDs (avoid D0/D1 because you may want RX/TX later)
const uint8_t LED_DIR1_PIN = 2; // D2
const uint8_t LED_DIR2_PIN = 3; // D3
const unsigned int LED_BLINK_ON_MS = 150;
const unsigned int LED_BLINK_OFF_MS = 150;
// Serial output control (0 = no output, 1 = output)
bool SerialOutputAllow = true;
// ---- Calibration parameters ----
const uint16_t AverageRepeat = 100; // number of init measurements
const float VdiffThresholdMargin = 1.50f; // multiplier (e.g., 1.5 = +50% margin)
const float VdiffThresholdMinVolts = 0.03f; // floor in volts
// Globals: latest measured averaged voltages
float v1 = 0.0f;
float v2 = 0.0f;
// Calibration globals
float VdiffBaseline = 0.0f; // median(|V1-V2|) measured at boot (no movement)
float VdiffThreshold = 0.0f; // final threshold used for direction decision
// Direction encoding:
// 0 = NONE, 2 = DIR1, 3 = DIR2
byte direction = 0;
static inline float absf(float x) { return (x >= 0.0f) ? x : -x; }
static inline float maxf(float a, float b) { return (a > b) ? a : b; }
// -----------------------------------------------------------------------------
// LED helpers
// -----------------------------------------------------------------------------
static void blinkLED(uint8_t pin, uint8_t times)
{
for (uint8_t i = 0; i < times; i++) {
digitalWrite(pin, HIGH);
delay(LED_BLINK_ON_MS);
digitalWrite(pin, LOW);
delay(LED_BLINK_OFF_MS);
}
}
static void initBlinkSequence()
{
// LED at Output 2 blinks 2 times
blinkLED(LED_DIR1_PIN, 2);
// LED at Output 3 blinks 3 times
blinkLED(LED_DIR2_PIN, 3);
}
static void applyDirectionOutputs()
{
digitalWrite(LED_DIR1_PIN, LOW);
digitalWrite(LED_DIR2_PIN, LOW);
if (direction == 2) {
digitalWrite(LED_DIR1_PIN, HIGH);
} else if (direction == 3) {
digitalWrite(LED_DIR2_PIN, HIGH);
}
}
// -----------------------------------------------------------------------------
// Measurement
// -----------------------------------------------------------------------------
static void measureAveragedVoltages()
{
unsigned long startTime = millis();
unsigned long sum1 = 0;
unsigned long sum2 = 0;
unsigned long samples = 0;
while (millis() - startTime < SAMPLE_WINDOW_MS) {
sum1 += analogRead(ADC_PIN_1);
sum2 += analogRead(ADC_PIN_2);
samples++;
delayMicroseconds(SAMPLE_DELAY_US);
}
float avg1 = (float)sum1 / samples;
float avg2 = (float)sum2 / samples;
// Convert ADC value to voltage (default analog reference = Vcc ~ 5V)
v1 = avg1 * (5.0f / 1023.0f);
v2 = avg2 * (5.0f / 1023.0f);
}
// -----------------------------------------------------------------------------
// Median helpers (for calibration)
// -----------------------------------------------------------------------------
static void sortFloatArray(float *a, uint16_t n)
{
// Insertion sort (n=100 is small, OK)
for (uint16_t i = 1; i < n; i++) {
float key = a[i];
int16_t j = (int16_t)i - 1;
while (j >= 0 && a[j] > key) {
a[j + 1] = a[j];
j--;
}
a[j + 1] = key;
}
}
static float medianOfSortedFloatArray(const float *a, uint16_t n)
{
if (n == 0) return 0.0f;
if (n & 1) {
return a[n / 2];
} else {
return (a[(n / 2) - 1] + a[n / 2]) * 0.5f;
}
}
// Calibrate baseline + threshold once at boot/reset
// Parameter must be only AverageRepeat.
static void calibrateVdiffThreshold(uint16_t averageRepeat)
{
if (averageRepeat < 1) averageRepeat = 1;
if (averageRepeat > 200) averageRepeat = 200; // RAM safety cap
float diffs[200];
for (uint16_t i = 0; i < averageRepeat; i++) {
measureAveragedVoltages();
diffs[i] = absf(v1 - v2); // baseline mismatch sample
}
sortFloatArray(diffs, averageRepeat);
VdiffBaseline = medianOfSortedFloatArray(diffs, averageRepeat);
// Final threshold with margin + floor
VdiffThreshold = maxf(VdiffBaseline * VdiffThresholdMargin, VdiffThresholdMinVolts);
}
// -----------------------------------------------------------------------------
// State machine
// -----------------------------------------------------------------------------
static void updateDirectionFromVoltages()
{
const float diff = v1 - v2;
const float absDiff = absf(diff);
// Neutral region: hold direction
if (absDiff <= VdiffThreshold) {
return;
}
// Clear dominance: set direction deterministically
// diff > 0 => v1 >> v2 => DIR1 (2)
// diff < 0 => v2 >> v1 => DIR2 (3)
direction = (diff > 0.0f) ? (byte)2 : (byte)3;
}
// -----------------------------------------------------------------------------
// Serial output
// -----------------------------------------------------------------------------
static void serialPrintAll()
{
if (!SerialOutputAllow) return;
Serial.print("A2 for PB3 XTAL1, Pin2: ");
Serial.print(v1, 3);
Serial.print(" V | ");
Serial.print("A3 for PB4 XTAL2, Pin3: ");
Serial.print(v2, 3);
Serial.print(" V | ");
Serial.print("|V1-V2|: ");
Serial.print(absf(v1 - v2), 3);
Serial.print(" V | ");
Serial.print("VdiffBaseline: ");
Serial.print(VdiffBaseline, 3);
Serial.print(" V | ");
Serial.print("VdiffThresholdMargin: ");
Serial.print(VdiffThresholdMargin, 2);
Serial.print(" x | ");
Serial.print("VdiffThresholdMinVolts: ");
Serial.print(VdiffThresholdMinVolts, 3);
Serial.print(" V | ");
Serial.print("VdiffThreshold: ");
Serial.print(VdiffThreshold, 3);
Serial.print(" V | ");
Serial.print("direction: ");
Serial.println(direction);
}
// -----------------------------------------------------------------------------
// Arduino entry points
// -----------------------------------------------------------------------------
void setup() {
Serial.begin(115200);
pinMode(ADC_PIN_1, INPUT);
pinMode(ADC_PIN_2, INPUT);
pinMode(LED_DIR1_PIN, OUTPUT);
pinMode(LED_DIR2_PIN, OUTPUT);
// Initial calibration (assumes no movement)
calibrateVdiffThreshold(AverageRepeat);
// Init blink sequence: 2x on D2, 3x on D3
initBlinkSequence();
// One measurement for initial display + initial direction
measureAveragedVoltages();
updateDirectionFromVoltages();
applyDirectionOutputs();
if (SerialOutputAllow) {
Serial.println("=== DenshaBekutoru 0004 Version 0.1 Test (Arduino Uno Test) ===");
Serial.println(" ~~~ InitStart ~~~");
Serial.print(" AverageRepeat: ");
Serial.println(AverageRepeat);
Serial.println(" ~~~ InitEnd ~~~");
}
serialPrintAll();
}
void loop() {
measureAveragedVoltages();
updateDirectionFromVoltages();
applyDirectionOutputs();
serialPrintAll();
delay(300);
}

View File

@ -0,0 +1,286 @@
/*
0004_DenshaBekutoru Optocoupler Averaging Test
Target dev board (later): Arduino Pro Mini 5V / 16 MHz (ATmega328P)
----------------------------------------------------------------------------
State machine (direction memory) + Hysteresis (Version A)
----------------------------------------------------------------------------
We use two thresholds:
T_hold (smaller): below -> treat as neutral and HOLD state
T_enter (larger): above -> allow direction change (set by sign)
Behavior:
If |diff| <= T_hold : hold direction
If |diff| >= T_enter: set direction by sign
Else (between) : hold direction
Presets (implemented as multipliers of the calibrated VdiffThreshold):
T_hold = 1.0 * VdiffThreshold
T_enter = 2.0 * VdiffThreshold
Inputs derived from v1, v2:
diff = v1 - v2
*/
const unsigned long SAMPLE_WINDOW_MS = 100; // averaging window per measurement
const unsigned long SAMPLE_DELAY_US = 500; // delay between ADC samples (~2 kHz)
const uint8_t ADC_PIN_1 = A2; // Pro Mini A2 -> later ATtiny85 PB3 (XTAL1, Pin 2)
const uint8_t ADC_PIN_2 = A3; // Pro Mini A3 -> later ATtiny85 PB4 (XTAL2, Pin 3)
// Direction LEDs (avoid D0/D1 because you may want RX/TX later)
const uint8_t LED_DIR1_PIN = 2; // D2
const uint8_t LED_DIR2_PIN = 3; // D3
const unsigned int LED_BLINK_ON_MS = 150;
const unsigned int LED_BLINK_OFF_MS = 150;
// Serial output control (0 = no output, 1 = output)
bool SerialOutputAllow = true;
// ---- Calibration parameters ----
const uint16_t AverageRepeat = 100; // number of init measurements
const float VdiffThresholdMargin = 1.50f; // multiplier (e.g., 1.5 = +50% margin)
const float VdiffThresholdMinVolts = 0.03f; // floor in volts
// ---- Hysteresis presets (multipliers of VdiffThreshold) ----
const float THOLD_MULT = 1.0f; // T_hold = THOLD_MULT * VdiffThreshold
const float TENTER_MULT = 2.0f; // T_enter = TENTER_MULT * VdiffThreshold
// Globals: latest measured averaged voltages
float v1 = 0.0f;
float v2 = 0.0f;
// Calibration globals
float VdiffBaseline = 0.0f; // median(|V1-V2|) measured at boot (no movement)
float VdiffThreshold = 0.0f; // calibrated base threshold (used to derive T_hold/T_enter)
// Hysteresis thresholds (derived once after calibration)
float T_hold = 0.0f;
float T_enter = 0.0f;
// Direction encoding:
// 0 = NONE, 2 = DIR1, 3 = DIR2
byte direction = 0;
static inline float absf(float x) { return (x >= 0.0f) ? x : -x; }
static inline float maxf(float a, float b) { return (a > b) ? a : b; }
// -----------------------------------------------------------------------------
// LED helpers
// -----------------------------------------------------------------------------
static void blinkLED(uint8_t pin, uint8_t times)
{
for (uint8_t i = 0; i < times; i++) {
digitalWrite(pin, HIGH);
delay(LED_BLINK_ON_MS);
digitalWrite(pin, LOW);
delay(LED_BLINK_OFF_MS);
}
}
static void initBlinkSequence()
{
// LED at Output 2 blinks 2 times
blinkLED(LED_DIR1_PIN, 2);
// LED at Output 3 blinks 3 times
blinkLED(LED_DIR2_PIN, 3);
}
static void applyDirectionOutputs()
{
digitalWrite(LED_DIR1_PIN, LOW);
digitalWrite(LED_DIR2_PIN, LOW);
if (direction == 2) {
digitalWrite(LED_DIR1_PIN, HIGH);
} else if (direction == 3) {
digitalWrite(LED_DIR2_PIN, HIGH);
}
}
// -----------------------------------------------------------------------------
// Measurement
// -----------------------------------------------------------------------------
static void measureAveragedVoltages()
{
unsigned long startTime = millis();
unsigned long sum1 = 0;
unsigned long sum2 = 0;
unsigned long samples = 0;
while (millis() - startTime < SAMPLE_WINDOW_MS) {
sum1 += analogRead(ADC_PIN_1);
sum2 += analogRead(ADC_PIN_2);
samples++;
delayMicroseconds(SAMPLE_DELAY_US);
}
float avg1 = (float)sum1 / samples;
float avg2 = (float)sum2 / samples;
// Convert ADC value to voltage (default analog reference = Vcc ~ 5V)
v1 = avg1 * (5.0f / 1023.0f);
v2 = avg2 * (5.0f / 1023.0f);
}
// -----------------------------------------------------------------------------
// Median helpers (for calibration)
// -----------------------------------------------------------------------------
static void sortFloatArray(float *a, uint16_t n)
{
// Insertion sort (n=100 is small, OK)
for (uint16_t i = 1; i < n; i++) {
float key = a[i];
int16_t j = (int16_t)i - 1;
while (j >= 0 && a[j] > key) {
a[j + 1] = a[j];
j--;
}
a[j + 1] = key;
}
}
static float medianOfSortedFloatArray(const float *a, uint16_t n)
{
if (n == 0) return 0.0f;
if (n & 1) {
return a[n / 2];
} else {
return (a[(n / 2) - 1] + a[n / 2]) * 0.5f;
}
}
// Calibrate baseline + VdiffThreshold once at boot/reset
static void calibrateVdiffThreshold(uint16_t averageRepeat)
{
if (averageRepeat < 1) averageRepeat = 1;
if (averageRepeat > 200) averageRepeat = 200; // RAM safety cap
float diffs[200];
for (uint16_t i = 0; i < averageRepeat; i++) {
measureAveragedVoltages();
diffs[i] = absf(v1 - v2); // baseline mismatch sample
}
sortFloatArray(diffs, averageRepeat);
VdiffBaseline = medianOfSortedFloatArray(diffs, averageRepeat);
// Calibrated base threshold with margin + floor
VdiffThreshold = maxf(VdiffBaseline * VdiffThresholdMargin, VdiffThresholdMinVolts);
// Derive hysteresis thresholds (Version A)
T_hold = THOLD_MULT * VdiffThreshold;
T_enter = TENTER_MULT * VdiffThreshold;
}
// -----------------------------------------------------------------------------
// State machine with hysteresis (Version A)
// -----------------------------------------------------------------------------
static void updateDirectionFromVoltages()
{
const float diff = v1 - v2;
const float absDiff = absf(diff);
// 1) Neutral region: HOLD
if (absDiff <= T_hold) {
return;
}
// 2) Strong region: allow direction change
if (absDiff >= T_enter) {
direction = (diff > 0.0f) ? (byte)2 : (byte)3;
return;
}
// 3) Deadband between T_hold and T_enter: HOLD
return;
}
// -----------------------------------------------------------------------------
// Serial output
// -----------------------------------------------------------------------------
static void serialPrintAll()
{
if (!SerialOutputAllow) return;
Serial.print("A2 for PB3 XTAL1, Pin2: ");
Serial.print(v1, 3);
Serial.print(" V | ");
Serial.print("A3 for PB4 XTAL2, Pin3: ");
Serial.print(v2, 3);
Serial.print(" V | ");
Serial.print("|V1-V2|: ");
Serial.print(absf(v1 - v2), 3);
Serial.print(" V | ");
Serial.print("VdiffBaseline: ");
Serial.print(VdiffBaseline, 3);
Serial.print(" V | ");
Serial.print("VdiffThreshold: ");
Serial.print(VdiffThreshold, 3);
Serial.print(" V | ");
Serial.print("T_hold: ");
Serial.print(T_hold, 3);
Serial.print(" V | ");
Serial.print("T_enter: ");
Serial.print(T_enter, 3);
Serial.print(" V | ");
Serial.print("direction: ");
Serial.println(direction);
}
// -----------------------------------------------------------------------------
// Arduino entry points
// -----------------------------------------------------------------------------
void setup() {
Serial.begin(115200);
pinMode(ADC_PIN_1, INPUT);
pinMode(ADC_PIN_2, INPUT);
pinMode(LED_DIR1_PIN, OUTPUT);
pinMode(LED_DIR2_PIN, OUTPUT);
// Initial calibration (assumes no movement)
calibrateVdiffThreshold(AverageRepeat);
// Init blink sequence: 2x on D2, 3x on D3
initBlinkSequence();
// One measurement for initial display + initial direction
measureAveragedVoltages();
updateDirectionFromVoltages();
applyDirectionOutputs();
if (SerialOutputAllow) {
Serial.println("=== DenshaBekutoru Optocoupler Average Test (Arduino Pro Mini 5V/16MHz) ===");
Serial.print("AverageRepeat: ");
Serial.println(AverageRepeat);
Serial.print("THOLD_MULT: ");
Serial.print(THOLD_MULT, 2);
Serial.print(" TENTER_MULT: ");
Serial.println(TENTER_MULT, 2);
}
serialPrintAll();
}
void loop() {
measureAveragedVoltages();
updateDirectionFromVoltages();
applyDirectionOutputs();
serialPrintAll();
delay(300);
}

View File

@ -0,0 +1,231 @@
/*
0004_DenshaBekutoru State Machine implemented
Target dev board (later): Arduino Pro Mini 5V / 16 MHz (ATmega328P)
----------------------------------------------------------------------------
State machine (direction memory)
----------------------------------------------------------------------------
Inputs derived from v1, v2, VdiffThreshold:
N (Neutral): |v1 - v2| <= VdiffThreshold
D1 (v1>>v2): (v1 - v2) > VdiffThreshold
D2 (v2>>v1): (v2 - v1) > VdiffThreshold
State encoding (kept transfer-friendly, and compatible with earlier pin-mapping):
0 = NONE
2 = DIR1
3 = DIR2
Transition table:
Current \ Input | N (neutral) | D1 (v1>>v2) | D2 (v2>>v1)
-----------------------------------------------------------------
0 (NONE) | 0 | 2 | 3
2 (DIR1) | 2 | 2 | 3
3 (DIR2) | 3 | 2 | 3
Mermaid (documentation)
-----------------------
stateDiagram-v2
[*] --> NONE
state "0 : NONE" as NONE
state "2 : DIR1 (v1>>v2)" as DIR1
state "3 : DIR2 (v2>>v1)" as DIR2
NONE --> NONE : |v1 - v2| <= T
NONE --> DIR1 : v1 - v2 > T
NONE --> DIR2 : v2 - v1 > T
DIR1 --> DIR1 : |v1 - v2| <= T
DIR1 --> DIR1 : v1 - v2 > T
DIR1 --> DIR2 : v2 - v1 > T
DIR2 --> DIR2 : |v1 - v2| <= T
DIR2 --> DIR2 : v2 - v1 > T
DIR2 --> DIR1 : v1 - v2 > T
*/
const unsigned long SAMPLE_WINDOW_MS = 100; // averaging window per measurement
const unsigned long SAMPLE_DELAY_US = 500; // delay between ADC samples (~2 kHz)
const uint8_t ADC_PIN_1 = A2; // Pro Mini A2 -> later ATtiny85 PB3 (XTAL1, Pin 2)
const uint8_t ADC_PIN_2 = A3; // Pro Mini A3 -> later ATtiny85 PB4 (XTAL2, Pin 3)
// Serial output control (0 = no output, 1 = output)
bool SerialOutputAllow = true;
// ---- Calibration parameters ----
const uint16_t AverageRepeat = 100; // number of init measurements
const float VdiffThresholdMargin = 1.50f; // multiplier (e.g., 1.5 = +50% margin)
const float VdiffThresholdMinVolts = 0.03f; // floor in volts (optional but practical)
// Globals: latest measured averaged voltages
float v1 = 0.0f;
float v2 = 0.0f;
// Calibration globals
float VdiffBaseline = 0.0f; // median(|V1-V2|) measured at boot (no movement)
float VdiffThreshold = 0.0f; // final threshold used for direction decision
// Direction encoding:
// 0 = NONE, 2 = DIR1, 3 = DIR2
byte direction = 0;
static inline float absf(float x) { return (x >= 0.0f) ? x : -x; }
static inline float maxf(float a, float b) { return (a > b) ? a : b; }
// Measure averaged voltages (updates globals v1/v2)
static void measureAveragedVoltages()
{
unsigned long startTime = millis();
unsigned long sum1 = 0;
unsigned long sum2 = 0;
unsigned long samples = 0;
while (millis() - startTime < SAMPLE_WINDOW_MS) {
sum1 += analogRead(ADC_PIN_1);
sum2 += analogRead(ADC_PIN_2);
samples++;
delayMicroseconds(SAMPLE_DELAY_US);
}
float avg1 = (float)sum1 / samples;
float avg2 = (float)sum2 / samples;
// Convert ADC value to voltage (default analog reference = Vcc ~ 5V)
v1 = avg1 * (5.0f / 1023.0f);
v2 = avg2 * (5.0f / 1023.0f);
}
static void sortFloatArray(float *a, uint16_t n)
{
// Insertion sort (n=100 is small, OK)
for (uint16_t i = 1; i < n; i++) {
float key = a[i];
int16_t j = (int16_t)i - 1;
while (j >= 0 && a[j] > key) {
a[j + 1] = a[j];
j--;
}
a[j + 1] = key;
}
}
static float medianOfSortedFloatArray(const float *a, uint16_t n)
{
if (n == 0) return 0.0f;
if (n & 1) {
return a[n / 2];
} else {
return (a[(n / 2) - 1] + a[n / 2]) * 0.5f;
}
}
// Calibrate baseline + threshold once at boot/reset
// Parameter must be only AverageRepeat (per your requirement).
static void calibrateVdiffThreshold(uint16_t averageRepeat)
{
if (averageRepeat < 1) averageRepeat = 1;
if (averageRepeat > 200) averageRepeat = 200; // RAM safety cap
float diffs[200];
for (uint16_t i = 0; i < averageRepeat; i++) {
measureAveragedVoltages();
diffs[i] = absf(v1 - v2); // baseline mismatch sample
}
sortFloatArray(diffs, averageRepeat);
VdiffBaseline = medianOfSortedFloatArray(diffs, averageRepeat);
// Final threshold with margin + floor
VdiffThreshold = maxf(VdiffBaseline * VdiffThresholdMargin, VdiffThresholdMinVolts);
}
// Implements the state machine table above
static void updateDirectionFromVoltages()
{
const float diff = v1 - v2;
const float absDiff = absf(diff);
// Neutral region: hold direction (only NONE stays NONE)
if (absDiff <= VdiffThreshold) {
// direction remains unchanged (0 stays 0; 2 stays 2; 3 stays 3)
return;
}
// Clear dominance: set direction deterministically
// diff > 0 => v1 >> v2 => DIR1 (2)
// diff < 0 => v2 >> v1 => DIR2 (3)
direction = (diff > 0.0f) ? (byte)2 : (byte)3;
}
static void serialPrintAll()
{
if (!SerialOutputAllow) return;
Serial.print("A2 for PB3 XTAL1, Pin2: ");
Serial.print(v1, 3);
Serial.print(" V | ");
Serial.print("A3 for PB4 XTAL2, Pin3: ");
Serial.print(v2, 3);
Serial.print(" V | ");
Serial.print("|V1-V2|: ");
Serial.print(absf(v1 - v2), 3);
Serial.print(" V | ");
Serial.print("VdiffBaseline: ");
Serial.print(VdiffBaseline, 3);
Serial.print(" V | ");
Serial.print("VdiffThresholdMargin: ");
Serial.print(VdiffThresholdMargin, 2);
Serial.print(" x | ");
Serial.print("VdiffThresholdMinVolts: ");
Serial.print(VdiffThresholdMinVolts, 3);
Serial.print(" V | ");
Serial.print("VdiffThreshold: ");
Serial.print(VdiffThreshold, 3);
Serial.print(" V | ");
Serial.print("direction: ");
Serial.println(direction);
}
void setup() {
Serial.begin(115200);
pinMode(ADC_PIN_1, INPUT);
pinMode(ADC_PIN_2, INPUT);
// Initial calibration: compute VdiffThreshold once after boot/reset
calibrateVdiffThreshold(AverageRepeat);
// One measurement for initial display
measureAveragedVoltages();
updateDirectionFromVoltages();
if (SerialOutputAllow) {
Serial.println("=== DenshaBekutoru State Machine and Variable Output (Arduino Pro Mini 5V/16MHz) ===");
Serial.println("~~~ Init ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
Serial.print("AverageRepeat: ");
Serial.println(AverageRepeat);
Serial.println("~~~ Init ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
}
serialPrintAll();
}
void loop() {
measureAveragedVoltages();
updateDirectionFromVoltages();
serialPrintAll();
delay(300);
}