Related
Hi
I'm new to this android development.
I've read a lot on the net how to do it, but I can't get the speed I want/expect.
In init:
bitmap = Bitmap.createBitmap(WIDTH, HEIGHT, Bitmap.Config.ARGB_8888);
my onDraw method:
protected void onDraw(Canvas canvas) {
for (int j = 0; j < HEIGHT-2; j++) {
int heat = 0;
for (int i = 0; i < WIDTH; i++) {
heat = firebuf[j];
bitmap.setPixel(i, j, palbuf[heat]);
}
}
canvas.drawBitmap(bitmap, 0, 0, null);
// refresh the canvas
invalidate();
}
Is this the way to do it?
I come from the PC world, where I would have 2 buffers directly to the gfx card. Write in one while the other is shown and then switch.
Couldn't find out how to do it on the android.
Nobody knows how to update the sceren fast?
no idea... doesnt look like it ... most the ppl here doing roms/kernel dev... very few app developers
Okay thanks
there are android dev forums ...
and lots of info at developer.android.com
id ask arround there
Hey all,
Is there someone who knows how I can disable the HTC back-key, the start-menu key and the Home-key? Those keys aren't applicationkey's like I'd expected and so I can't disable them using UnregisterFunc1() (extern void of coredll.dll).
Anyone who knows how I can disable this keys in C#?
Thanks,
MadMatt
Does SetWindowHookEx work for you?
.h:
Code:
#define WH_KEYBOARD_LL 20
extern "C" {
typedef LRESULT (CALLBACK* HOOKPROC)(int code, WPARAM wParam, LPARAM lParam);
typedef struct tagKBDLLHOOKSTRUCT
{
DWORD vkCode; // virtual key code
DWORD scanCode; // scan code DWORD flags; // flags
DWORD flags; // unused
DWORD time; // time stamp for this message
DWORD dwExtraInfo; // extra info from the driver or keybd_event
}
KBDLLHOOKSTRUCT, *PKBDLLHOOKSTRUCT;
HHOOK
WINAPI
SetWindowsHookExW(
int idHook,
HOOKPROC lpfn,
HINSTANCE hmod,
DWORD dwThreadId);
#define SetWindowsHookEx SetWindowsHookExW
BOOL
WINAPI
UnhookWindowsHookEx(
HHOOK hhk);
LRESULT
WINAPI
CallNextHookEx(
HHOOK hhk,
int nCode,
WPARAM wParam,
LPARAM lParam);
}
#endif
Usage is well-written on MSDN. Though, it won't work if keypad driver doesn't send this button to the system.
Then, better way, but it will need some investigation. Not sure if it works for these keys, but last HTC devices have keypad.dll with global key hook feature. You can use it like this:
Code:
HANDLE kbd = CreateFileW(L"KBD1:", 0xC0000000, 0, 0, 3, 0, 0);
DWORD returned;
DeviceIoControl(kbd, 0xB2028, <<unique ID of hook>>, 4, 0, 0, &returned, 0);
Then your application should wait for "GetFromKeyEvent_<<ID>>" event. GetEventData will return keycode. After that you should use SetEvent for "SendBackToKeyEvent_<<ID>>" event with the same data. Not sure if it is fully correct description - I can't check it since my driver doesn't have this feature.
That's they way how HTCVolumeControl gets info about pressed volume keys.
Thanks! I'll take a look at this! I think my device should support this feature, as it is released in May 2010, isn't it?
If it doesn't work, I'll post again
I'm developing a mirror for lazer beam(Ball sprite). There I'm trying to redirect the laze beam according to the ration degree of the mirror(Rectangle). How can I collide the ball to the correct angle if the colliding object is with some angle(45 deg) rather than colliding back.
Here is my complete code
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace collision
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D ballTexture;
Rectangle ballBounds;
Vector2 ballPosition;
Vector2 ballVelocity;
float ballSpeed = 30f;
Texture2D blockTexture;
Rectangle blockBounds;
Vector2 blockPosition;
private Vector2 origin;
KeyboardState keyboardState;
//Font
SpriteFont Font1;
Vector2 FontPos;
private String displayText;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
/// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
// TODO: Add your initialization logic here
ballPosition = new Vector2(this.GraphicsDevice.Viewport.Width / 2,
this.GraphicsDevice.Viewport.Height * 0.25f);
blockPosition = new Vector2(this.GraphicsDevice.Viewport.Width / 2,
this.GraphicsDevice.Viewport.Height /2);
ballVelocity = new Vector2(0, 1);
base.Initialize();
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
ballTexture = Content.Load<Texture2D>("ball");
blockTexture = Content.Load<Texture2D>("mirror");
//create rectangles based off the size of the textures
ballBounds = new Rectangle((int)(ballPosition.X - ballTexture.Width / 2),
(int)(ballPosition.Y - ballTexture.Height / 2), ballTexture.Width, ballTexture.Height);
blockBounds = new Rectangle((int)(blockPosition.X - blockTexture.Width / 2),
(int)(blockPosition.Y - blockTexture.Height / 2), blockTexture.Width, blockTexture.Height);
origin.X = blockTexture.Width / 2;
origin.Y = blockTexture.Height / 2;
// TODO: use this.Content to load your game content here
Font1 = Content.Load<SpriteFont>("SpriteFont1");
FontPos = new Vector2(graphics.GraphicsDevice.Viewport.Width - 100, 20);
}
/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// all content.
/// </summary>
protected override void UnloadContent()
{
// TODO: Unload any non ContentManager content here
}
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
///
private float RotationAngle;
float circle = MathHelper.Pi * 2;
float angle;
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
// TODO: Add your update logic here
//check for collision between the ball and the block, or if the ball is outside the bounds of the screen
if (ballBounds.Intersects(blockBounds) || !GraphicsDevice.Viewport.Bounds.Contains(ballBounds))
{
//we have a simple collision!
//if it has hit, swap the direction of the ball, and update it's position
ballVelocity = -ballVelocity;
ballPosition += ballVelocity * ballSpeed;
}
else
{
//move the ball a bit
ballPosition += ballVelocity * ballSpeed;
}
//update bounding boxes
ballBounds.X = (int)ballPosition.X;
ballBounds.Y = (int)ballPosition.Y;
blockBounds.X = (int)blockPosition.X;
blockBounds.Y = (int)blockPosition.Y;
keyboardState = Keyboard.GetState();
float val = 1.568017f/90;
if (keyboardState.IsKeyDown(Keys.Space))
RotationAngle = RotationAngle + (float)Math.PI;
if (keyboardState.IsKeyDown(Keys.Left))
RotationAngle = RotationAngle - val;
angle = (float)Math.PI / 4.0f; // 90 degrees
RotationAngle = angle;
// RotationAngle = RotationAngle % circle;
displayText = RotationAngle.ToString();
base.Update(gameTime);
}
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: Add your drawing code here
spriteBatch.Begin();
// Find the center of the string
Vector2 FontOrigin = Font1.MeasureString(displayText) / 2;
spriteBatch.DrawString(Font1, displayText, FontPos, Color.White, 0, FontOrigin, 1.0f, SpriteEffects.None, 0.5f);
spriteBatch.Draw(ballTexture, ballPosition, Color.White);
spriteBatch.Draw(blockTexture, blockPosition,null, Color.White, RotationAngle,origin, 1.0f, SpriteEffects.None, 0f);
spriteBatch.End();
base.Draw(gameTime);
}
}
}
Do you have a github repo? I would like to help out but your wording is slightly confusing. You may have to update your collision to reflect changes in the X and Y axes seperately. I can try to replicate this and PM you my solution
This guide is about floating, moving a point in coordinate system.
It may be useful to make spirit level (bubble level) or magic 8 ball and so on
Just use device's accelerometer sensor to moving a center point.
I wrote android custom view which implements SensorEventListener.
Do some initialization (measuring screen size, set boundary size, assign values...) first.
Draw the x, y axis and boundary and little circle.
In end of the onDraw(), invalidate() makes little circle keep moving.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Important formula to make little circle inside circular boundary is below.
Code:
private void calc(){
//for simulating object floating on water
//against gravity
xCon += mSensorX;
yCon -= mSensorY;
/*
//for simulating object rolling on ground
//adjust to gravity
xCon -= mSensorX;
yCon += mSensorY;
*/
//for circular boundary
if(Math.pow(xCon, 2) + Math.pow(yCon, 2) >= boundarySquare){
isBoundaryOut = true;
[COLOR="SeaGreen"] if(xCon != 0 && yCon != 0){
radian = (float) Math.atan2(yCon, xCon);
}[/COLOR]
[COLOR="RoyalBlue"] xCon = (float) (Math.cos(radian) * boundary);
yCon = (float) (Math.sin(radian) * boundary);[/COLOR]
}
else{
isBoundaryOut = false;
}
}
add the sensor's value to x, y coordinate (xCon, yCon) and check it is out of the circular boundary.
If it is change the value with the Formula
x = cos(atan2(y, x)) * CIRCULAR_BOUDNARY_RADIUS
y = sin(atan2(y, x)) * CIRCULAR_BOUDNARY_RADIUS
You can select the circle to float on water or roll on ground just change addition <-> subtraction.
- For simulating object floating on water, against gravity
xCon += mSensorX;
yCon -= mSensorY;
- For simulating object rolling on ground, adjust to gravity
xCon -= mSensorX;
yCon += mSensorY;
Also you can change boundary shape easily
For rectangle boundary
if(xCon > horizontalBound){
xCon = horizontalBound;
}
else if(xCon < -horizontalBound){
xCon = -horizontalBound;
}
if(yCon > verticalBound){
yCon = verticalBound;
}
else if(yCon < -verticalBound){
yCon = -verticalBound;
}
Code:
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER)
return;
mSensorX = sensor_weight * event.values[0];
mSensorY = sensor_weight * event.values[1];
}
onSensorChanged() I just add sensor values(weighted) to center point x, y and it seems to be quite enough to do rough simulation.
Whole source code of my custom view is like below.
Code:
package net.gerosyab.circularboundary;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.util.AttributeSet;
import android.view.Display;
import android.view.Surface;
import android.view.View;
import android.view.WindowManager;
public class MyView extends View implements SensorEventListener{
Context context;
//weight for calculating speed of floating image
//multiplied with accelrometer sensor value
//faster if it is more than 1
//slower if it is less than 1
float sensor_weight = 2.15f;
float width;
float height;
float cx;
float cy;
float x;
float y;
float xCon, yCon;
float boundary;
float boundarySquare;
float dotRadius = 15;
float radian;
Paint linePaint, circlePaint, dotPaint, textPaint;
float horizontalBound;
float verticalBound;
boolean isBoundaryOut = false;
private float mSensorX;
private float mSensorY;
private SensorManager mSensorManager;
private Sensor mAccelerometer;
private WindowManager mWindowManager;
private Display mDisplay;
public MyView(Context context) {
super(context);
this.context = context;
init();
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
init();
}
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.context = context;
init();
}
private void init(){
linePaint = new Paint();
circlePaint = new Paint();
dotPaint = new Paint();
textPaint = new Paint();
linePaint.setColor(Color.WHITE );
linePaint.setAntiAlias(true);
linePaint.setStrokeWidth(2);
linePaint.setStyle(Paint.Style.STROKE);
circlePaint.setColor(Color.YELLOW);
circlePaint.setAntiAlias(true);
circlePaint.setStrokeWidth(2);
circlePaint.setStyle(Paint.Style.STROKE);
dotPaint.setColor(Color.RED);
dotPaint.setAntiAlias(true);
dotPaint.setStrokeWidth(5);
dotPaint.setStyle(Paint.Style.FILL);
textPaint.setColor(Color.WHITE);
textPaint.setAntiAlias(true);
textPaint.setStyle(Paint.Style.FILL_AND_STROKE);
textPaint.setTextSize(40);
x = cx;
y = cy;
xCon = 0;
yCon = 0;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//draw x, y axis
canvas.drawLine(cx, 0, cx, height, linePaint);
canvas.drawLine(0, cy, width, cy, linePaint);
//draw circular boundary
canvas.drawCircle(cx, cy, boundary, circlePaint);
canvas.drawRect(cx - horizontalBound, cy - verticalBound, cx + horizontalBound, cy + verticalBound, circlePaint);
calc();
//draw dot
canvas.drawCircle(xCon + cx, yCon + cy, dotRadius, dotPaint);
//draw text
canvas.drawText("isBoundaryOut : " + isBoundaryOut, 100, 50, textPaint);
canvas.drawText("sensorX : " + mSensorX, 100, 100, textPaint);
canvas.drawText("sensorY : " + mSensorY, 100, 150, textPaint);
canvas.drawText("xCon : " + xCon, 100, 200, textPaint);
canvas.drawText("yCon : " + yCon, 100, 250, textPaint);
canvas.drawText("cx : " + cx, 100, 300, textPaint);
canvas.drawText("cy : " + cy, 100, 350, textPaint);
canvas.drawText("horizontalBound : " + horizontalBound, 100, 400, textPaint);
canvas.drawText("verticalBound : " + verticalBound, 100, 450, textPaint);
invalidate();
}
private void calc(){
//for simulating object floating on water
//against gravity
xCon += mSensorX;
yCon -= mSensorY;
/*
//for simulating object rolling on ground
//adjust to gravity
xCon -= mSensorX;
yCon += mSensorY;
*/
/*
//for rectangle boundary
if(xCon > horizontalBound){
xCon = horizontalBound;
}
else if(xCon < -horizontalBound){
xCon = -horizontalBound;
}
if(yCon > verticalBound){
yCon = verticalBound;
}
else if(yCon < -verticalBound){
yCon = -verticalBound;
}
*/
//for circular boundary
if(Math.pow(xCon, 2) + Math.pow(yCon, 2) >= boundarySquare){
isBoundaryOut = true;
if(xCon != 0 && yCon != 0){
radian = (float) Math.atan2(yCon, xCon);
}
xCon = (float) (Math.cos(radian) * boundary);
yCon = (float) (Math.sin(radian) * boundary);
}
else{
isBoundaryOut = false;
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = getWidth();
height = getHeight();
cx = width / 2;
cy = height / 2;
boundary = width * 0.15f;
horizontalBound = boundary;
verticalBound = boundary;
boundarySquare = boundary * boundary;
mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
// Get an instance of the WindowManager
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
mDisplay = mWindowManager.getDefaultDisplay();
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_UI);
}
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER)
return;
switch (mDisplay.getRotation()) {
case Surface.ROTATION_0:
mSensorX = sensor_weight * event.values[0];
mSensorY = sensor_weight * event.values[1];
break;
case Surface.ROTATION_90:
mSensorX = sensor_weight * -event.values[1];
mSensorY = sensor_weight * event.values[0];
break;
case Surface.ROTATION_180:
mSensorX = sensor_weight * -event.values[0];
mSensorY = sensor_weight * -event.values[1];
break;
case Surface.ROTATION_270:
mSensorX = sensor_weight * event.values[1];
mSensorY = sensor_weight * -event.values[0];
break;
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mSensorManager.unregisterListener(this);
}
}
Hope this is helpful!
Thanks
For some reason, my MTCD unit with Malaysk Rom did not allow me to set long press function to a steering wheel button set for volume. Not sure if it is Malaysk rom or factory rom, or MCU not is causing this issue.
So, in order to fix this problem, I used an Arduino to interpret steering wheel button inputs and send different signal for short press and long press to the head unit.
Also, my car does not have steering wheel radio buttons, so I utilized cruise control buttons.
I'm sure it would be possible to do the same in the head unit's firmware it self, but I did not know where to begin, so I chose the path I know will work.
I check long press for buttons I will be using for volume only (Up and down) and other buttons are just passed to reflect actual button status.
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
This is the circuit I have built. Very simple circuit with a digital potentiometer to send different resistance for each button press.
This is the after soldering parts. Ignore wires at the bottom. I didn't realized copied version of Arduino pro mini did not come with bootloader preloaded, so I had to tap into some pins to program the chip directly first using avr programmer.
Added a 100 ohm resistor to a relay in order to protect it from higher voltage normally present in an automobile. The absolute maximum voltage of the relay I used was 15V and normal operating voltage is 12V. I did not want risk breaking it.
This button allows me to select between cruise control and remote control. This is what controls the relay in the circuit.
Hook up everything, and it is good to go. I used ACC power to power the circuit, since there is no reason to keep it running when radio is not on.
Up -> Volume Up
Long Up (1 sec) -> Previous
2+ Sec Up -> Repeat Long Up every sec
Down -> Volume Down
Long Down (1 sec) -> Next
2+ Sec Down -> Repeat Long Down every sec
Pull -> Play/Pause
Push -> Mode
Long Push -> Home (Using head unit's built in long press button assignment).
Following is my code. I'm sure it is not the best code, but it works.
Code:
// Car Steering Wheel Rmote w/ Cruise Control Buttons
// lambition
// 10/31/2016
// Using SPI controlled Microchip MCP41100 100K Digital Potentiometer for Output.
// Resistance Value calculated
// Termianl W => Ground, Terminal A => Head Unit Key Input, Terminal B => Leave open
// R_WA: Resistance between Wiper and terminal A.
// R_AB: Resistance between terminal A and B. (100k)
// R_W: Wiper resistance. 125 for 100k chip.
// R_WA(D_n) = ((R_AB * (256-D_n))/256+R_W
// 390.6 ohms per step (100k/256).
// Pin connections
// CS(1) -> 10 (CS)
// SCK(2) -> 13 (SCK)
// SI(3) -> 11 (MOSI)
// Vss (4) -> GND
// PA (5) -> Head Unit Key 1
// PW (6) -> GND
// PB (7) -> Open
// Vdd (8) -> 5V
// First byte is command, Second byte is data. MSB first.
// Command Byte
// X X C1 C0 X X P1 P0
// C1,C0 = 0,1: Write Data, 1,0: Shutdown, Ignore others.
// P1,P0 = 0,1: Select Pot 0, 1,0: Select Pot 1, 1,1: Select Both (P1 is don't care bit for MCP41xxx)
#define CMD_B 0b00010001 // Write Data, Select Pot0. Don't need any other commands.
// Map I/O Pins
#define KEYIN A0 // Key Input
#define CS 10 // Chip select
// Hold time for each button press except pull and push
#define button_hold_ms 500
/*
Input Key Resistance Values in Ohms. Toyota Highalnder Cruise Control Switches.
UP 240
DOWN 630
PULL 1540
PUSH 0
*/
// Input Key Voltages with 1k pull up resistor
// Change these values to what ever your switch is giving.
#define KEYUP_V 1
#define KEYDOWN_V 2
#define KEYPULL_V 3
#define KEYPUSH_V 0
#define VAL_PER_V 204.6
#define TOLERANCE_V 0.35
#define OFFSET 0.05
// Calculate minimum and maximum ADC values
#define MIN_KEYUP_VAL (int)((KEYUP_V+OFFSET-TOLERANCE_V)*VAL_PER_V)
#define MAX_KEYUP_VAL (int)((KEYUP_V+OFFSET+TOLERANCE_V)*VAL_PER_V)
#define MIN_KEYDOWN_VAL (int)((KEYDOWN_V+OFFSET-TOLERANCE_V)*VAL_PER_V)
#define MAX_KEYDOWN_VAL (int)((KEYDOWN_V+OFFSET+TOLERANCE_V)*VAL_PER_V)
#define MIN_KEYPULL_VAL (int)((KEYPULL_V+OFFSET-TOLERANCE_V)*VAL_PER_V)
#define MAX_KEYPULL_VAL (int)((KEYPULL_V+OFFSET+TOLERANCE_V)*VAL_PER_V)
#define MIN_KEYPUSH_VAL (int)((KEYPUSH_V+OFFSET-TOLERANCE_V)*VAL_PER_V)
#define MAX_KEYPUSH_VAL (int)((KEYPUSH_V+OFFSET+TOLERANCE_V)*VAL_PER_V)
// Digital Potentiometer Values
#define KEYUP_POT 255
#define KEYDOWN_POT 230
#define KEYLONGUP_POT 200
#define KEYLONGDOWN_POT 160
#define KEYPUSH_POT 120
#define KEYPULL_POT 0
#include <SPI.h>
enum KeyType { OPEN, KEYUP, KEYDOWN, KEYLONGUP, KEYLONGDOWN, KEYPUSH, KEYPULL };
bool pot_off;
void setup() {
// put your setup code here, to run once:
// Configure Pins
pinMode(KEYIN, INPUT);
pinMode(CS, OUTPUT);
digitalWrite(CS, HIGH);
// SPI Initialization
SPI.begin();
DigitalPot_Shutdown(); // Start with Pot off
}
void loop() {
// put your main code here, to run repeatedly:
static KeyType current_button_state = OPEN;
static KeyType prev_button_state = OPEN;
static KeyType tmp_prev_button_state = OPEN;
static unsigned long button_pressed = 0;
static unsigned long millis_button_held;
static long keyInputValue;
static bool over_sec_processed = true;
static bool prev_long_press = false;
keyInputValue = analogRead(KEYIN); // Read Analog Input
KeyType tmp_button_state ;
// Decode Key Inputs
if ( (keyInputValue <= MAX_KEYUP_VAL) && (keyInputValue >= MIN_KEYUP_VAL)) // Key Up Detected
{
tmp_button_state = KEYUP;
}
else if ( (keyInputValue <= MAX_KEYDOWN_VAL) && (keyInputValue >= MIN_KEYDOWN_VAL)) // Key Down Detected
{
tmp_button_state = KEYDOWN;
}
else if ( (keyInputValue <= MAX_KEYPULL_VAL) && (keyInputValue >= MIN_KEYPULL_VAL)) // Key Pull Detected
{
tmp_button_state = KEYPULL;
}
else if ( (keyInputValue <= MAX_KEYPUSH_VAL) && (keyInputValue >= MIN_KEYPUSH_VAL)) // Key Push Detected
{
tmp_button_state = KEYPUSH;
}
else
{
tmp_button_state = OPEN; // Mark key not pressed and Ignore out of spec values.
}
// Input State Changes
if (tmp_button_state != tmp_prev_button_state)
{
button_pressed = millis();
over_sec_processed = false;
}
if ((millis() - button_pressed) > 10) // 10 ms debounce delay
{
current_button_state = tmp_button_state;
}
tmp_prev_button_state = tmp_button_state;
if ( (current_button_state != OPEN) && (current_button_state == prev_button_state) ) // Button Held
{
millis_button_held = millis() - button_pressed;
}
if ( (current_button_state == OPEN)) // Key released
{
if ((millis_button_held < 1000) && (!prev_long_press)) // Key was pressed less than 1 second and previous button was not a long press.
{
if (prev_button_state == KEYUP) // If last button pressed was Keyup send keyup
{
DigitalPot_Write(KEYUP_POT);
delay(button_hold_ms);
DigitalPot_Shutdown();
}
else if (prev_button_state == KEYDOWN) // If last button pressed was Keydown send keydown.
{
DigitalPot_Write(KEYDOWN_POT);
delay(button_hold_ms);
DigitalPot_Shutdown();
}
else if (!pot_off) // For all other keys, shutoff pot upon releasing button, unless already off.
{
DigitalPot_Shutdown();
}
}
prev_long_press = false;
}
else if ( (current_button_state == KEYUP) || (current_button_state == KEYDOWN) ) // Key up or down
{
if ( (millis_button_held >= 1000) && (!over_sec_processed) ) // Over 1 second
{
if (prev_button_state == KEYUP)
{
DigitalPot_Write(KEYLONGUP_POT);
delay(button_hold_ms);
DigitalPot_Shutdown();
}
else if (prev_button_state == KEYDOWN)
{
DigitalPot_Write(KEYLONGDOWN_POT);
delay(button_hold_ms);
DigitalPot_Shutdown();
}
over_sec_processed = true;
prev_long_press = true;
}
if (millis_button_held >= 2000) // Over 2 seconds
{
delay(1000); // Wait 1 sec between signal
over_sec_processed = false; // Send
}
}
else if ( current_button_state == KEYPULL )
{
if (pot_off) // Write to Pot only once
{
DigitalPot_Write(KEYPULL_POT);
}
prev_long_press = false;
}
else if ( current_button_state == KEYPUSH )
{
if (pot_off) // Write to Pot only once
{
DigitalPot_Write(KEYPUSH_POT);
}
prev_long_press = false;
}
prev_button_state = current_button_state;
}
void DigitalPot_Write(const byte data)
{
SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0)); //10MHz, MSB First, Ouput on rising edge.
digitalWrite(CS, LOW);
SPI.transfer(CMD_B); // Send Command
SPI.transfer(data); // send Data
digitalWrite(CS, HIGH);
SPI.endTransaction();
pot_off = false;
}
void DigitalPot_Shutdown()
{
SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0)); //10MHz, MSB First, Ouput on rising edge.
digitalWrite(CS, LOW);
SPI.transfer(0b00100001); //Shutdown Command
SPI.transfer(0); // Dummy data
digitalWrite(CS, HIGH);
SPI.endTransaction();
pot_off = true;
}
Dude... That is f** awesome!! But I hardly doubt that anyone will be willing to go through the same just to get the long-press function =)) I ll keep waiting for the software solution
Kudos!! Awesome job and very simple - but you just have to come up with the idea.
Respect!
Mighty_X said:
Dude... That is f** awesome!! But I hardly doubt that anyone will be willing to go through the same just to get the long-press function =)) I ll keep waiting for the software solution
Click to expand...
Click to collapse
It is actually quiet simple. Whole thing cost me about $6 to make because I already had switches and relay. Chinese copy version of Arduino pro mini is only around $3 or less. Digital potentiometer is around $3 as well, but this can be replaced with resistor array and using 5 digital IO pins.
Digital potentiometer just makes it easier to change resistance values.
Henkdrenth said:
Kudos!! Awesome job and very simple - but you just have to come up with the idea.
Respect!
Click to expand...
Click to collapse
I wish someone cracks MCU of our device. Our device have great potential, but software is made to just barely functioning.
Hi guys i would ask your help. I have an Alfa Romeo Giulietta, Installed in the dash a nexus 7 2013 and I want use the steering wheel buttons. The car use the protocol is 15765-4 29bit and I have on Bluetooth obd reader. Can you please help me out? Thanks in advance.
Inviato dal mio FRD-L09 utilizzando Tapatalk
valk791 said:
Hi guys i would ask your help. I have an Alfa Romeo Giulietta, Installed in the dash a nexus 7 2013 and I want use the steering wheel buttons. The car use the protocol is 15765-4 29bit and I have on Bluetooth obd reader. Can you please help me out? Thanks in advance.
Inviato dal mio FRD-L09 utilizzando Tapatalk
Click to expand...
Click to collapse
You might receive suggestions posting in the appropriate forum or opening a new thread. To post here is isnt appropriate.
Amazing work.... Many thanks
Wow a nice, clean, elegant and simple solution.
I've done it little bit different (Old Style) as my car model didn't had factory Steering wheel, so I made steering wheel upgrade that left me with 6 Wires to play with (3 for 6 Media Commands and 3 for 4 Cruise control). So I ended up with dismantle steering wheel commands setting up each command different by 20kOhm in parallel to outputs So I have 6 Main commands and any combination of two+ keys simultaneously for additional commands as I get 1/2 of sum Ohm of the commands pressed. and o the other side 4 Cruise control commands (do not have cruise control in Car) both commands Directly connected GND+KEY1 for Media Controls and GND+KEY2 for cruise control.
Now I only need to think how to emulate keyboard commands so that I can navigate Home Screen and emulate Swipe.
FYI I have not seen any ROM to allow Vol +/- long press as it is reserved for Vol Repeat mode
Works great for me, just so others may also take advantage of this code, I used the below for a Honda Jazz/Fit 2010 with a Kenwood Android Auto Head Unit, I only used the CH+/- and MODE for long press as I did not want to mess with volume triggers and 8 total controls is enough for me (just), not sure if anyone else had an issue but for me the lower resistance (higher potentiometer values) were the only ones that would work with my head unit.
Code:
// Car Steering Wheel Rmote w/ Cruise Control Buttons
// lambition
// 10/31/2016
// Using SPI controlled Microchip MCP41100 100K Digital Potentiometer for Output.
// Resistance Value calculated
// Termianl W => Ground, Terminal A => Head Unit Key Input, Terminal B => Leave open
// R_WA: Resistance between Wiper and terminal A.
// R_AB: Resistance between terminal A and B. (100k)
// R_W: Wiper resistance. 125 for 100k chip.
// R_WA(D_n) = ((R_AB * (256-D_n))/256+R_W
// 390.6 ohms per step (100k/256).
// Pin connections
// CS(1) -> 10 (CS)
// SCK(2) -> 13 (SCK)
// SI(3) -> 11 (MOSI)
// Vss (4) -> GND
// PA (5) -> Head Unit Key 1
// PW (6) -> GND
// PB (7) -> Open
// Vdd (8) -> 5V
// First byte is command, Second byte is data. MSB first.
// Command Byte
// X X C1 C0 X X P1 P0
// C1,C0 = 0,1: Write Data, 1,0: Shutdown, Ignore others.
// P1,P0 = 0,1: Select Pot 0, 1,0: Select Pot 1, 1,1: Select Both (P1 is don't care bit for MCP41xxx)
#define CMD_B 0b00010001 // Write Data, Select Pot0. Don't need any other commands.
// Map I/O Pins
#define KEYIN A0 // Key Input
#define CS 10 // Chip select
// Hold time for each button press except pull and push
#define button_hold_ms 500
//Just for programming head unit
//#define button_hold_ms 3000
// Input Key Voltages with 1k pull up resistor
// Change these values to what ever your switch is giving.
#define INPUT_KEYUP_V 315
#define INPUT_KEYDOWN_V 115
#define INPUT_KEYUP_CH 691
#define INPUT_KEYDOWN_CH 501
#define INPUT_KEY_MODE 839
#define TOLERANCE_V 20
#define OFFSET 0.05
// Calculate minimum and maximum ADC values
#define MIN_KEYUP_V_VAL (int)(INPUT_KEYUP_V+OFFSET-TOLERANCE_V)
#define MAX_KEYUP_V_VAL (int)(INPUT_KEYUP_V+OFFSET+TOLERANCE_V)
#define MIN_KEYDOWN_V_VAL (int)(INPUT_KEYDOWN_V+OFFSET-TOLERANCE_V)
#define MAX_KEYDOWN_V_VAL (int)(INPUT_KEYDOWN_V+OFFSET+TOLERANCE_V)
#define MIN_KEYUP_CH_VAL (int)(INPUT_KEYUP_CH+OFFSET-TOLERANCE_V)
#define MAX_KEYUP_CH_VAL (int)(INPUT_KEYUP_CH+OFFSET+TOLERANCE_V)
#define MIN_KEYDOWN_CH_VAL (int)(INPUT_KEYDOWN_CH+OFFSET-TOLERANCE_V)
#define MAX_KEYDOWN_CH_VAL (int)(INPUT_KEYDOWN_CH+OFFSET+TOLERANCE_V)
#define MIN_KEY_MODE_VAL (int)(INPUT_KEY_MODE+OFFSET-TOLERANCE_V)
#define MAX_KEY_MODE_VAL (int)(INPUT_KEY_MODE+OFFSET+TOLERANCE_V)
// Digital Potentiometer Values
#define KEYUP_V_POT 255
#define KEYDOWN_V_POT 254
#define KEYUP_CH_POT 250
#define KEYDOWN_CH_POT 243
#define KEYUPLONG_CH_POT 239
#define KEYDOWNLONG_CH_POT 246
#define KEY_MODE_POT 94
#define KEYLONG_MODE_POT 200
#include <SPI.h>
enum KeyType { OPEN, KEYUP_V, KEYDOWN_V, KEYUP_CH, KEYDOWN_CH, KEYLONGUP_CH, KEYLONGDOWN_CH, KEY_MODE, KEYLONG_MODE };
bool pot_off;
bool debug = false;
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
if (debug) {
while (!Serial) { }
}
debugMessage("BOOT: Serial initialized");
// Configure Pins
pinMode(KEYIN, INPUT);
pinMode(CS, OUTPUT);
digitalWrite(CS, HIGH);
// SPI Initialization
SPI.begin();
DigitalPot_Shutdown(); // Start with Pot off
debugMessage("BOOT: Booted");
}
void loop() {
// put your main code here, to run repeatedly:
static KeyType current_button_state = OPEN;
static KeyType prev_button_state = OPEN;
static KeyType tmp_prev_button_state = OPEN;
static unsigned long button_pressed = 0;
static unsigned long millis_button_held;
static long keyInputValue;
static bool over_sec_processed = true;
static bool prev_long_press = false;
keyInputValue = analogRead(KEYIN); // Read Analog Input
//debugMessage((String)keyInputValue);
KeyType tmp_button_state ;
// Decode Key Inputs
if ( (keyInputValue <= MAX_KEYUP_V_VAL) && (keyInputValue >= MIN_KEYUP_V_VAL)) // Key Up Detected
{
tmp_button_state = KEYUP_V;
}
else if ( (keyInputValue <= MAX_KEYDOWN_V_VAL) && (keyInputValue >= MIN_KEYDOWN_V_VAL)) // Key Down Detected
{
tmp_button_state = KEYDOWN_V;
}
else if ( (keyInputValue <= MAX_KEYUP_CH_VAL) && (keyInputValue >= MIN_KEYUP_CH_VAL)) // Key Pull Detected
{
tmp_button_state = KEYUP_CH;
}
else if ( (keyInputValue <= MAX_KEYDOWN_CH_VAL) && (keyInputValue >= MIN_KEYDOWN_CH_VAL)) // Key Push Detected
{
tmp_button_state = KEYDOWN_CH;
}
else if ( (keyInputValue <= MAX_KEY_MODE_VAL) && (keyInputValue >= MIN_KEY_MODE_VAL)) // Key Push Detected
{
tmp_button_state = KEY_MODE;
}
else
{
tmp_button_state = OPEN; // Mark key not pressed and Ignore out of spec values.
}
// Input State Changes
if (tmp_button_state != tmp_prev_button_state)
{
button_pressed = millis();
over_sec_processed = false;
}
if ((millis() - button_pressed) > 10) // 10 ms debounce delay
{
current_button_state = tmp_button_state;
}
tmp_prev_button_state = tmp_button_state;
if ( (current_button_state != OPEN) && (current_button_state == prev_button_state) ) // Button Held
{
millis_button_held = millis() - button_pressed;
}
//Button is now in open
if ( (current_button_state == OPEN)) // Key released
{
//debugMessage("STATE: OPEN");
if ((millis_button_held < 1000) && (!prev_long_press)) // Key was pressed less than 1 second and previous button was not a long press.
{
if (prev_button_state == KEYUP_CH) // If last button pressed was Keyup send keyup
{
debugMessage("ACTION: SHORT KEYUP_CH");
DigitalPot_Write(KEYUP_CH_POT);
delay(button_hold_ms);
DigitalPot_Shutdown();
}
else if (prev_button_state == KEYDOWN_CH) // If last button pressed was Keydown send keydown.
{
debugMessage("ACTION: SHORT KEYDOWN_CH");
DigitalPot_Write(KEYDOWN_CH_POT);
delay(button_hold_ms);
DigitalPot_Shutdown();
}
else if (prev_button_state == KEY_MODE) // If last button pressed was Keydown send keydown.
{
debugMessage("ACTION: SHORT KEYMODE");
DigitalPot_Write(KEY_MODE_POT);
delay(button_hold_ms);
DigitalPot_Shutdown();
}
else if (!pot_off) // For all other keys, shutoff pot upon releasing button, unless already off.
{
DigitalPot_Shutdown();
}
}
prev_long_press = false;
}
else if ( (current_button_state == KEYUP_CH) || (current_button_state == KEYDOWN_CH) || (current_button_state == KEY_MODE) ) // Key up or down
{
if ( (millis_button_held >= 1000) && (!over_sec_processed) ) // Over 1 second
{
if (prev_button_state == KEYUP_CH)
{
debugMessage("ACTION: LONG KEYUP_CH");
DigitalPot_Write(KEYUPLONG_CH_POT);
delay(button_hold_ms);
DigitalPot_Shutdown();
}
else if (prev_button_state == KEYDOWN_CH)
{
debugMessage("ACTION: LONG KEYDOWN_CH");
DigitalPot_Write(KEYDOWNLONG_CH_POT);
delay(button_hold_ms);
DigitalPot_Shutdown();
}
else if (prev_button_state == KEY_MODE)
{
debugMessage("ACTION: LONG KEYMODE");
DigitalPot_Write(KEYLONG_MODE_POT);
delay(button_hold_ms);
DigitalPot_Shutdown();
}
over_sec_processed = true;
prev_long_press = true;
}
if (millis_button_held >= 2000) // Over 2 seconds
{
delay(1000); // Wait 1 sec between signal
over_sec_processed = false; // Send
}
}
else if ( current_button_state == KEYUP_V )
{
if (pot_off) // Write to Pot only once
{
debugMessage("ACTION: SHORT KEYUP_V");
DigitalPot_Write(KEYUP_V_POT);
}
prev_long_press = false;
}
else if ( current_button_state == KEYDOWN_V )
{
if (pot_off) // Write to Pot only once
{
debugMessage("ACTION: SHORT KEYDOWN_V");
DigitalPot_Write(KEYDOWN_V_POT);
}
prev_long_press = false;
}
prev_button_state = current_button_state;
}
void DigitalPot_Write(const byte data)
{
SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0)); //10MHz, MSB First, Ouput on rising edge.
digitalWrite(CS, LOW);
SPI.transfer(CMD_B); // Send Command
SPI.transfer(data); // send Data
digitalWrite(CS, HIGH);
SPI.endTransaction();
pot_off = false;
}
void DigitalPot_Shutdown()
{
SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0)); //10MHz, MSB First, Ouput on rising edge.
digitalWrite(CS, LOW);
SPI.transfer(0b00100001); //Shutdown Command
SPI.transfer(0); // Dummy data
digitalWrite(CS, HIGH);
SPI.endTransaction();
pot_off = true;
}
void debugMessage(String message) {
if (debug) {
Serial.println(message);
}
}
My car is hyundai santafe 2010 with steering wheel button, I measure the Resistance Value between button and ground by pressing each button, the result are:
- Mode: 2.10 K ohm
- Up: 434 ohm
- Down: 1.12 K ohm
- Volume Up: 4.60 K ohm
- Volume Down: 6.81 K ohm
- Map: 10.72 K ohm
- Phone On: 41.00 K ohm
- Phone Off: 18.99 K ohm
I replace the other head unit (10" monitor of Suzuki XL7), so I want to use the steering wheel control of my Santafe to control the Suzuki XL7 head unit
The Suzuki XL7 head unit use Resistance Value for each steering wheel button are:
- Mute: 56 +- 0.6 Ohm
- Volume +: 131 +- 1.3 Ohm
- Volume -: 241 +- 2.4 Ohm
- Next: 1571 +- 15.7 Ohm
- Prev: 751 +- 7.5 Ohm
- Mode: 421 +- 4.2 Ohm
I read this thread many times and prepare
- Arduino Nano.
- resistor: 1K ohm
- MCP41100
- Converter 12v to 5V.
but I still not understand some code and parameter.
sparky3387, lambitioncan you explain more detail for me
the diagram of MCP41100 to Arduino and head unit key?
// MCP41100 8 Pin connections
// CS(1) -> D10 (Arduino)
// SCK(2) -> D13 (Arduino)
// SI(3) -> D11 (Arduino)
// Vss (4) -> GND
// PA (5) -> Head Unit Key 1
// PW (6) -> GND
// PB (7) -> Open
// Vdd (8) -> 5V
The value of INPUT_KEY is voltage or Resistance Value
// Input Key Voltages with 1k pull up resistor
// Change these values to what ever your switch is giving.
#define INPUT_KEYUP_V 315
#define INPUT_KEYDOWN_V 115
#define INPUT_KEYUP_CH 691
#define INPUT_KEYDOWN_CH 501
#define INPUT_KEY_MODE 839
Do I must change the value:
#define TOLERANCE_V 20
#define OFFSET 0.05
Can you add code to measure the value ò INPUT_KEY like this link:
Resistor Ladder Steering Wheel Control Interpreter Using Arduino
I’m currently busy creating an Arduino steering wheel adapter between my ’05 Pontiac GTO steering wheel controls and the Parrot Asteroid Smart Android-powered head unit. Doing this beca…
atomic-cactus.com
How do I do, please help me.
Thank you.