From ec46ff0810a05d97d6604af75dd7fbad130a40e5 Mon Sep 17 00:00:00 2001 From: Jan-Simon Moeller Date: Fri, 2 Sep 2022 00:05:07 +0200 Subject: Initial version of Steering Wheel Microcontroller Firmware Reads all buttons and sends out CAN bus messages similar to the previous LIN converter. Best use thonny as IDE/Editor. Board is available on ttyACM0. Source code is stored on the board itself as: - boot.py (blinks board at boot) - main.py (main script that runs by default) The steering wheel decoder is in the steering-wheel subfolder. The monitor folder contains a simple serial monitor printing the CAN message. You need a CANpico board for this. The firmware used/tested with this code is in the firmware-CANpico subfolder. Signed-off-by: Jan-Simon Moeller Change-Id: I9751b6e0e7a2fc6cb6c82bb0a64719c6aa6ebe11 --- .gitreview | 5 + LICENSE | 23 +++ README.md | 0 firmware-CANpico/LICENSE | 3 + firmware-CANpico/firmware-20220805.uf2 | Bin 0 -> 637440 bytes src/monitor/boot.py | 23 +++ src/monitor/main.py | 19 +++ src/steering-wheel/boot.py | 22 +++ src/steering-wheel/main.py | 249 +++++++++++++++++++++++++++++++++ 9 files changed, 344 insertions(+) create mode 100644 .gitreview create mode 100644 LICENSE create mode 100644 README.md create mode 100644 firmware-CANpico/LICENSE create mode 100644 firmware-CANpico/firmware-20220805.uf2 create mode 100644 src/monitor/boot.py create mode 100644 src/monitor/main.py create mode 100644 src/steering-wheel/boot.py create mode 100644 src/steering-wheel/main.py diff --git a/.gitreview b/.gitreview new file mode 100644 index 0000000..c4a292e --- /dev/null +++ b/.gitreview @@ -0,0 +1,5 @@ +[gerrit] +host=gerrit.automotivelinux.org +port=29418 +project=src/steering-wheel-microcontroller +defaultbranch=master diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..09d59dc --- /dev/null +++ b/LICENSE @@ -0,0 +1,23 @@ +Unless otherwise stated in the (sub-)folder or file, this software +is under the Apache-2.0 license. + +The shorthand used in the files is: + +SPDX-License-Identifier: Apache-2.0 + + +The license statement is below: + +Copyright 2022 The Linux Foundation + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/firmware-CANpico/LICENSE b/firmware-CANpico/LICENSE new file mode 100644 index 0000000..04fe80b --- /dev/null +++ b/firmware-CANpico/LICENSE @@ -0,0 +1,3 @@ +As per https://docs.micropython.org/en/v1.19.1/license.html , +the micropython sources that are used to build this firmware +are under MIT License. \ No newline at end of file diff --git a/firmware-CANpico/firmware-20220805.uf2 b/firmware-CANpico/firmware-20220805.uf2 new file mode 100644 index 0000000..9d99dcd Binary files /dev/null and b/firmware-CANpico/firmware-20220805.uf2 differ diff --git a/src/monitor/boot.py b/src/monitor/boot.py new file mode 100644 index 0000000..fe964f8 --- /dev/null +++ b/src/monitor/boot.py @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: Apache-2.0 +# (C) The Linux Foundation +# Author: Jan-Simon Moeller (jsmoeller@linuxfoundation.org) + +from machine import Pin +import time + +led = Pin(25, Pin.OUT) +i = 8 + + +led.value(0) + +while i > 0: + led.toggle() + time.sleep(0.1) + i = i-1 + + + + + + diff --git a/src/monitor/main.py b/src/monitor/main.py new file mode 100644 index 0000000..d3247ab --- /dev/null +++ b/src/monitor/main.py @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: Apache-2.0 +# (C) The Linux Foundation +# Author: Jan-Simon Moeller (jsmoeller@linuxfoundation.org) + +from rp2 import * +from time import sleep + + +led = Pin(25, Pin.OUT) +led.value(0) + +c = CAN() + +while True: + frames = c.recv() + for frame in frames: + led.toggle() + print(frame) + sleep(0.2) diff --git a/src/steering-wheel/boot.py b/src/steering-wheel/boot.py new file mode 100644 index 0000000..3cd5563 --- /dev/null +++ b/src/steering-wheel/boot.py @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: Apache-2.0 +# (C) The Linux Foundation +# Author: Jan-Simon Moeller (jsmoeller@linuxfoundation.org) + +from machine import Pin +import time + +led = Pin(25, Pin.OUT) +i = 8 + + +led.value(0) + +while i > 0: + led.toggle() + time.sleep(0.1) + i = i-1 + + + + + diff --git a/src/steering-wheel/main.py b/src/steering-wheel/main.py new file mode 100644 index 0000000..4a86ad8 --- /dev/null +++ b/src/steering-wheel/main.py @@ -0,0 +1,249 @@ +# SPDX-License-Identifier: Apache-2.0 +# (C) The Linux Foundation +# Author: Jan-Simon Moeller (jsmoeller@linuxfoundation.org) + +from machine import Pin +from machine import ADC +from time import sleep +import gc +from rp2 import * + +# Enable debug messages over serial (a bit slower) +DEBUG = 1 +DEBUG2 = 0 +# Enable canbus output +CANBUS = 1 +# enable only messages on changes +ONLYNEW = 1 + +# invert the button pins +INVERTPINS = 1 + +if CANBUS: + c = CAN() + led = Pin(25, Pin.OUT) + led.value(0) + +oldcandata = bytearray(8) + +# Hardware connection +HORN = Pin(18, Pin.IN, Pin.PULL_UP) +LANEASSIST = Pin(17, Pin.IN, Pin.PULL_UP) +INFOBUTTON = Pin(16, Pin.IN, Pin.PULL_UP) +AUDIOADC = ADC(26) +PHONEADC = ADC(27) +CRUISEADC = ADC(28) + +# ADC voltage/resolution conversion +conversion = 3.3/65535 + +def strhex(c): + return str("0x%02X" % int(c)) +def int2hex(c): + return str("%02X" % int(c)) + +myAUDIObutton = "NONE" +myPHONEbutton = "NONE" +myCRUISEbutton = "NONE" +myAUDIOcan = 0 +myPHONEcan = 0 +myCRUISEcan = 0 +myBUTTONcan = 0 + +while True: + # reset before read + myAUDIObutton = "NONE" + myPHONEbutton = "NONE" + myCRUISEbutton = "NONE" + myAUDIOcan = 0 + myPHONEcan = 0 + myCRUISEcan = 0 + myBUTTONcan = 0 + + # read values: + myLANE = (INVERTPINS - LANEASSIST.value())**2 + myINFO = (INVERTPINS - INFOBUTTON.value())**2 + myHORN = (INVERTPINS - HORN.value())**2 + myAUDIO = float("{:.2f}".format(float(AUDIOADC.read_u16())*conversion)) + myPHONE = float("{:.2f}".format(float(PHONEADC.read_u16())*conversion)) + myCRUISE = float("{:.2f}".format(float(CRUISEADC.read_u16())*conversion)) + + # Q'n'd spaghetticode for adc + # AUDIO + # Vin = 3.3V ADCx + # R1 = 680 Ohm | + # 3.3V o----[=R1=]---x---[=Steering Wheel=]---o GND + # + # Tipping point = ((V2a-V2b)/2)+V2b + # e.g. ((2.959-2.305)/2)+2.305 ~= 2.6 + # + # Button Resistance Voltage V2 tipping point + # No button pressed: 5.900 Ohm 2.959 -> 2.6 + # < : 1.577 Ohm 2.305 -> 2.0 + # > : 752 Ohm 1.733 -> 1.5 + # MODE: 421 Ohm 1.26 -> 1.0 + # - : 242 Ohm 0.866 -> 0.7 + # + : 132 Ohm 0.536 -> 0.4 + # mute: 57 Ohm 0.255 -> 0 + # + if float(myAUDIO) < 2.60: + myAUDIObutton = "<" + myAUDIOcan = 128 + # 0x80 + if float(myAUDIO) < 2.00: + myAUDIObutton = ">" + myAUDIOcan = 8 + #0x08 + if float(myAUDIO) < 1.50: + myAUDIObutton = "MODE" + myAUDIOcan = 32 + if float(myAUDIO) < 1.00: + myAUDIObutton = "-" + myAUDIOcan = 16 + if float(myAUDIO) < 0.70: + myAUDIObutton = "+" + myAUDIOcan = 64 + if float(myAUDIO) < 0.4: + myAUDIObutton = "MUTE" + myAUDIOcan = 1 + if not myINFO: + myAUDIOcan = 2 + + # Phone + # Vin = 3.3V ADCx + # R1 = 680 Ohm | + # 3.3V o----[=R1=]---x---[=Steering Wheel=]---o GND + # + # Tipping point = ((V2a-V2b)/2)+V2b + # e.g. ((2.91-1.7)/2)+1.7 ~= 2.3 + # + # Button Resistance Voltage V2 tipping point + # No button pressed: 5.180 Ohm 2.91 -> 2.3 + # Hang-up : 753 Ohm 1.7 -> 1.3 + # Take call : 242 Ohm 0.866 -> 0.56 + # Hands free/voice: 57 Ohm 0.255 + # + if float(myPHONE) < 2.30: + myPHONEbutton = "HANGUP" + myPHONEcan = 1 + if float(myPHONE) < 1.30: + myPHONEbutton = "TAKECALL" + myPHONEcan = 2 + if float(myPHONE) < 0.56: + myPHONEbutton = "HANDSFREE" + myPHONEcan = 4 + + # Cruise + # Vin = 3.3V ADCx + # R1 = 1000 Ohm | + # 3.3V o----[=R1=]---x---[=Steering Wheel=]---o GND + # + # Tipping point = ((V2a-V2b)/2)+V2b + # e.g. ((2.58-1.96)/2)+1.96 ~= 2.27 + # + # Button Resistance Voltage V2 tipping point + # No button pressed: 5.180 Ohm 2.58 -> 2.27 + # Distance : 1.463 Ohm 1.96 -> 1.70 + # Res/+ : 782 Ohm 1.45 -> 1.19 + # Set/-: 390 Ohm 0.93 -> 0.75 + # Limit : 211 Ohm 0.57 -> 0.44 + # Cancel : 101 Ohm 0.30 -> 0.15 + # Cruise Enable: 0 Ohm 0 !! + # + if float(myCRUISE) < 2.27: + myCRUISEbutton = "Distance" + myCRUISEcan = 1 + if float(myCRUISE) < 1.70: + myCRUISEbutton = "Res/+" + myCRUISEcan = 64 + if float(myCRUISE) < 1.19: + myCRUISEbutton = "Set/-" + myCRUISEcan = 16 + if float(myCRUISE) < 0.75: + myCRUISEbutton = "Limit" + myCRUISEcan = 2 + if float(myCRUISE) < 0.44: + myCRUISEbutton = "Cancel" + myCRUISEcan = 8 + if float(myCRUISE) < 0.15: + myCRUISEbutton = "CruiseEnable" + myCRUISEcan = 128 + + + if not myLANE: + myBUTTONcan = 1 + if not myINFO: + myBUTTONcan = 2 + if not myHORN: + myBUTTONcan = 128 + + if DEBUG: + print("LANE - %s" % ( 1 - myLANE ) ) + print("INFO - %s" % ( 1 - myINFO ) ) + print("HORN - %s" % ( 1 - myHORN ) ) + print("AUDIOADC - %s" % myAUDIO) + print("AUDIObutton %s" % myAUDIObutton) + print("PHONEADC - %s" % myPHONE) + print("PHONEbutton %s" % myPHONEbutton) + print("CRUISEADC - %s" % myCRUISE) + print("CRUISEbutton %s" % myCRUISEbutton) + print("") + print("ALLOC - %s" % gc.mem_alloc()) + print("FREE - %s" % gc.mem_free()) + print("") + + #CanMessage: + #Button ADC0 ADC1 ADC2 AUDIO PHONE CRUISE BUTTONS + # 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + if DEBUG2: + print( "CanMessage: %s,%s,%s,%s,%s,%s,%s,%s" % + ( strhex(myBUTTONcan), strhex(myAUDIO*10), strhex(myPHONE*10), strhex(myCRUISE*10), + strhex(myAUDIOcan), strhex(myPHONEcan), strhex(myCRUISEcan), strhex(myBUTTONcan) ) + ) + + if CANBUS: + # send out can message + canid = 0x21 +# candata = bytearray([myBUTTONcan, int(myAUDIO*10), +# int(myPHONE*10), int(myCRUISE*10), +# myAUDIOcan, myPHONEcan, +# myCRUISEcan, myBUTTONcan]) + candata = bytearray([myBUTTONcan, 0, + 0, 0, + myAUDIOcan, myPHONEcan, + myCRUISEcan, myBUTTONcan]) + if DEBUG2: + print(candata) + + if ONLYNEW: + # only send on differences + if not candata == oldcandata: + oldcandata = candata + f = CANFrame(CANID(canid), data=bytes(candata)) + led.toggle() + c.send_frame(f) + sleep(0.01) + while f.get_timestamp() is None: + print("*****************waiting for can frame to be sent") + sleep(0.1) + led.toggle() + else: + continue + else: + # always send + f = CANFrame(CANID(canid), data=bytes(candata)) + led.toggle() + c.send_frame(f) + sleep(0.01) + while f.get_timestamp() is None: + print("*****************waiting for can frame to be sent") + sleep(0.1) + led.toggle() + + else: + # do nothing + sleep(0.01) + sleep(0.04) + if DEBUG: + # debug output on serial is slower + sleep(0.05) \ No newline at end of file -- cgit 1.2.3-korg