Example Code for MLX10407

Hi,

I’ve been looking for a code example to make my MLX10407 run for quite a while but was stuck on some basic communication error.

Now I figured out how it works and just wanted to share the basic code.

Wiring just needs MOSI,SCK,SS,RST and power, of course.

Example is from a Adruino Nano

SS has to be HIGH to select the slave.

Tagwords: SPI, Speedometer, Tachometer, Tach, Tacho, Melexis, MLX 10407

Example rolls my speedo from 40-180 kmh.
Other devices have to be mapped, of course.

Map function in “Encode_Melexis” can be used for this.

Here the code :

#include <SPI.h>

const int slaveSelectPin = 10;
const int resetPin=7;

int increment = 1;
int kmh = 0;

void setup()
{
SPI.begin();
SPI.setBitOrder(MSBFIRST);
SPI.setDataMode(SPI_MODE0);
SPI.setClockDivider(SPI_CLOCK_DIV32);
digitalWrite(slaveSelectPin,LOW);
digitalWrite(resetPin,HIGH);

}

void loop(){
Encode_Melexis(kmh);
delay(30);
if (kmh> 180) increment = -1;
if (kmh < 40) increment = 1;
kmh = kmh + increment;
}

void Encode_Melexis(int speed1) {
byte Byte1;
byte Byte2;
byte Quadrant;
byte Header;
int Value;

Value = map (speed1, 20,230, 270,3560); //Map input value to speedo scale by experimenting
Quadrant = highByte(Value>>2) & 0b00000011; //Need those two bits for quadrant selection
Header = 0b10010000; // Bit 1 (D0) = Start Bit, Bit 2-4 (D1-3) = Select Logo

Byte1=Header | (highByte(Value<<2) & 0b00001111) ; //move lower bits to value range of MLX
Byte2=lowByte(Value<<2) | Quadrant; //Fill second byte with values and last 2 bits quadrant info

Write_Melexis(Byte1, Byte2);

}

int Write_Melexis(byte Byte1, byte Byte2) {
digitalWrite(slaveSelectPin,HIGH);
SPI.transfer(Byte1);
SPI.transfer(Byte2);
digitalWrite(slaveSelectPin,LOW);
}

Tried to compile your sketch but got the following errors.
What I initially wanted to do was create a serial print output so I could look at the values sent to the Arduino.

Using arduino 1.6.7, Teensy 3.1 (Never got far enough to actually write to the Teensy), USB Type:Serial.

Build options changed, rebuilding all

/Users/**********/Documents/Arduino/MLX10407_basic/MLX10407_basic.ino: In function ‘int Write_Melexis(byte, byte)’:

/Users/**********/Documents/Arduino/MLX10407_basic/MLX10407_basic.ino:52:1: warning: no return statement in function returning non-void [-Wreturn-type]
}
^

Sketch uses 14,228 bytes (5%) of program storage space. Maximum is 262,144 bytes.
Global variables use 3,540 bytes (5%) of dynamic memory, leaving 61,996 bytes for local variables. Maximum is 65,536 bytes.

That sketch compiles just fine in 1.6.7 so I suspect it has more to do with how you pasted it into the editor than anything else.

I’m guessing you don’t code much?

Now that someone else has posted original code that I can contribute to, my NDA doesn’t apply! :slight_smile: I will modify this code to be a little bit more useful and add some helpful comments in case you don’t want to wade through the data sheet.

SPI is helpful to get MLX10407 working, but not required. In fact, the MLX10407 does not use SPI. It uses 3-wire serial. It is nice and convenient to use SPI because it gives you a serial clock to easily work with that doesn’t require bit banging and the other pins can be helpful and directly mapped one to one (at least some of them, excluding slave select and reset of course.) There are multiple versions of the MLX 10407 data sheet out there and some are even erroneous which I suspect is contributing to some of the issues people are having using the chip.

The chip runs the 360 degree gauges with quadrants. Each quadrant takes a value between 0 and 511 (512 values total) which across all quadrants gives you a range of 2048 total values (0 through 2047.)

I found it easiest to write a function to handle just the input 0-2047 which then determines the appropriate quadrant moves the needle. The assumption is that 0 is at the very left side of the gauge with needle pointing straight left. Of course you can easily change that by either modifying code or by wiring the gauges up differently to the output of the chip. I’ve found most people frequently have problems with getting the gauge wiring correct and the second most common issue is not understanding the docs or looking at versions of docs that have it wrong. There is a particularly sinister version of the MLX 10407 data sheet that shows a sickeningly wrong pin out diagram for the chip (with multiple pins mislabeled) but then lists the pins by number correctly further down in a table.

To help with debugging, I wired my gauges so they come out just like the data sheet says for the Melexis with 0 degrees having the needle pointing straight to the left if you are looking at it head on.

The data sheet on this chip just plain sucks so you’ll have to get over that. There are two Vss pins. Pin 4 and Pin 22. You can use either one, but you don’t need both. There is one on each side for convenience. I wired Vcc and Vss both up to a +12V supply which Arduino supplies via their barrel jack. The Melexis and your controller need to share a common ground or you will have problems. So if you are powering everything including the gauges with that lousy 5V from the USB port on your Teensy you are in for some disappointing needle responsiveness unless you figure out a way to amplify the 5V from USB through a separate circuit to +12V. That provides sufficient gauge stiffness to provide accurate needle movement and I think the USB port could handle the additional current required to do so but it could be a bit tricky.

If you do the same, but the lowest value on your gauge is with the needle pointing toward the very bottom like in my case, then you can easily change it in software by replacing this line:

SPI.transfer(value << 3 | (newValue / 512));

with

SPI.transfer(value << 3 | (0x3 & 3 + (newValue / 512)));

or you could set it up so the needle is at it’s lowest value when it is pointing straight up:

SPI.transfer(value << 3 | (0x3 & 1 + (newValue / 512)));

or you could set it up so the needle is at it’s lowest value when it is pointing straight to the right:

SPI.transfer(value << 3 | (0x3 & 2 + (newValue / 512)));

Props to mandropil for finally providing some code to work with, something my NDA was preventing me from doing previously and something previous inquirers were unwilling or unable to provide.

Here is the modified and simplified code which has been tested and works properly on 1.6.7 with both my 360 degree gauges.

// SPI is helpful to get MLX10407 working, but not required. It is nice because
// it gives you a serial clock to easily work with and the other pins can be
// helpful but may not be. There are multiple versions of the MLX 10407 data
// sheet out there and some are even erroneous which I suspect is the source
// of most of the issues.

// Here are a couple of other really helpful hints:
// 1: You *really* need +12V connected to MLX pin 1 because if you give it less,
//   say 5V or 9V you will get lazy needle performance and when you set the
//   gauge, for example, to 50 degrees then where the needle falls will be
//   slightly different each time and will greatly depend on if the needle
//   was headed up from a lower angle like 30% or down from a larger angle
//   like 80 degrees.
// 2: Technically you can get away without connecting the ERR pin, but a
//   robus implementation would include and read it. You can probably use MOSI
//   for that. It seems to work OK for me on the Due board.

#include <SPI.h>

// Use defines for pin definitions instead of const ints
// defines are substituded at the precompile stage so
// they use no RAM on your device.
#define _MLX_CS_PIN          10    // Now wire arduino pin 10 to MLX pin 11
#define _MLX_RST_PIN         7     // Now wire arduino pin 7 to MLX pin 15
#define _MLX_START_BIT      0x80  // Prefixes all gauge updates

const byte gauge_lookup[6] = { 0x00, 0x10, 0x30, 0x40, 0x60, 0x50 };

void setup()
{
  // Don't forget to do this, one some boards the gauges will not respond if you leave it out
  pinMode(_MLX_CS_PIN, OUTPUT);
  pinMode(_MLX_RST_PIN, OUTPUT);

  // Setup SPI
  SPI.begin();
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE0);
  SPI.setClockDivider(SPI_CLOCK_DIV32); // wire SPI clock pin to MLX pin SCLK (1MHz is adequate, 4MHz is fine too)
  digitalWrite(_MLX_CS_PIN, LOW);       // MLX expects CS to go high on transfer then low to latch them the bytes in
  digitalWrite(_MLX_RST_PIN, HIGH);     // Reset has inverted logic and is active low
 
}

void loop(){
  int i = 0;

  for(i = 0; i < 2047; i++) {
    Write_Melexis(1, i);
    // You need at least 5us between transfers or the needles get unstable
    delayMicroseconds(5);
    Write_Melexis(2, i);

    // A 1ms delay is also useful for preventing eratic needle slap and
    // good continuity between values.
    delay(1);
  }

  for(i = 2047; i >= 0; i--) {
    Write_Melexis(1, i);
    delayMicroseconds(5);
    Write_Melexis(2, i);
    delay(1);
  }
}

// Normally for a multibyte transfer you would do SPI.transfer(b1, SPI_CONTINUE);
// then SPI.transfer(b2); However, since MLX is not really using SPI, the original
// version does work. However, for good form I am including the change.
// gaugeNumber should be 1 through 5 ( can make it zero based by removing first
// element of gauge_lookup array definition above.)
void Write_Melexis(int gaugeNumber, int newValue) {

  int value = newValue % 512; // This will produce the quandrant value between 0 and 511
  
  digitalWrite(_MLX_CS_PIN, HIGH);
  SPI.transfer(_MLX_START_BIT | gauge_lookup[gaugeNumber] | (value >> 5)) ; //, SPI_CONTINUE); 
  SPI.transfer(value << 3 | (newValue / 512));
  digitalWrite(_MLX_CS_PIN, LOW);
}

Reply with questions and maybe PM me as well. I’m pretty busy this week but I will try and help if I can.

Hello Exedor,

I really wondered why there was no working code example for that chip as it's a really good one for driving gauges.
Thank you for sharing in detail information! Some hints made my code for my car run better then before.
So for all other car enthusiasts who like to use the simplicity of the arduino and the MLX10407 this topic might get quite helpful.

Regards,
Mandropil

And @ Rjfon1

that compiler message is right. My example code defines the function as returning int but the function does not return anything.

Although does compile and run...