# How to increase the dimming range of mosfet controlled LED

I'm slowly getting into circuits and I've put together a PWM LED dimmer. The led is driven by this circuit:

I'm driving the PWM via a separate BLE device which maps a user input of [0, 100] to an 8 bit value for the arduinos PWM [0, 255]. Here is the code:

int led = 9;           // the PWM pin the LED is attached to
int intR;
int t;

void setup() {
Serial.begin(9600);
Serial.println("serial connected...");

// declare pin 9 to be an output:
pinMode(led, OUTPUT);

analogWrite(led, 0);
}

void loop() {
if (Serial.available() > 0) {
intR = Serial.parseInt();
t = map(intR, 0, 100, 0, 255); //remaps intR from [0, 100] -> [0, 255] to scale to PWM
Serial.print(intR);
Serial.print(" -> ");
Serial.print(t);
Serial.println("");

analogWrite(led, t);
}
}


Whenever I set the dim value to 100 (100% duty cycle), the led is shining quite brightly, but when I set the value to 0, the LED shines at probably 60% brightness.

What would I have to do in order to smoothly transition the LED from a fully OFF state, to a fully ON state?

EDIT:

After a discussion in the comments I've updated my code and circuit and something seems wrong. The following code and schematic pair give me the best dim range (visually maybe 10-100% total output) and works on multiple mosfets from my package.

New code & circuit schematic:

int led = 9; // the PWM pin the LED is attached to
int intR;

void setup() {
Serial.begin(9600);
Serial.println("serial connected...");

// declare pin 9 to be an output:
pinMode(led, OUTPUT);

digitalWrite(led, LOW);
}

void loop() {
if (Serial.available() > 0) {
intR = Serial.parseInt(); //reads the next valid integer
Serial.print(intR);
Serial.println("");

if (intR <= 255 && intR >= 0) {
if (intR == 0) {
digitalWrite(led, LOW);
} else {
analogWrite(led, intR);
}
}
}
}


My MOSFETs are labeled P30N06LE which correspond to this RFP30N06LE datasheet. And unless my fets were made incorrectly, my drain really does go straight to GND, and my LED negative feeds into the source. The problem I have now is twofold: 1) understanding why my circuit works this way and 2) getting my led to fully power OFF when setting the pin to LOW.

With the above configuration, when pin 9 is set to LOW, the LED is at ~10% brightness, and when I physically remove the wire from arduino pin 9, the LED shuts off completely. If I then plug the wire into various unpowered pins, the LED shines at a low brightness, indicating enough voltage is present to power the fet. How can I get the led to turn off completely?

• What MOSFET are you using? – τεκ Sep 1 '18 at 3:18
• I'm using a P30N06LE – GregoryNeal Sep 1 '18 at 3:26
• How fast are you sending serial data into your MCU? Also, are you sending that data in as a ramp (as in a sawtooth?) Or are you sending in the value 0 and holding it there (never changing the value) and still seeing the LED at about 60%? – jonk Sep 1 '18 at 4:04
• Can the LED take the full 12 volts? – Andy aka Sep 1 '18 at 7:53
• Are you sure you connected the MOSFET correctly? The LED should be connected to the center pin. – τεκ Sep 1 '18 at 12:51

How are you converting 0-100 to 0-255? It looks like you're using the external map function. All would be explained if the map function were "malfunctioning" and returning 155-255 for a 0-100 input. Since 155 is 60% of 255. Perhaps that function does not work the way you think it works.

Why not simply go t = intR * 255 / 100?

For that matter, why even input a 0-100 scale, think in binary and input a 0-255 scale.

• I've edited my code to remove the map function, however the behavior is still the same. inputting 255 seems to be full brightness, but inputting 0 is still almost full brightness, just a little bit dimmer. the code: void loop() { if (Serial.available() > 0) { intR = Serial.parseInt(); //reads the next valid integer Serial.print(intR); Serial.println(""); if (intR <= 255 && intR >= 0) { analogWrite(led, intR); } } } – GregoryNeal Sep 1 '18 at 15:55
• What numeral is the Arduino decoding? Any way to tell that? Does it work if you send a numeral 0 to the Arduino directly? What if you send 1? – Harper Sep 1 '18 at 16:00
• I've got an HM10 BLE module connected to the arduino. I'm using the android app BLE scanner (for testing). Once I connect I'm sending a write request to the HM10's custom characteristic. The arduino is reading the bits and parsing the integer and then sending the data straight to the PWM pin. Sending a value of 0 sets it to a fairly bright state. Then setting it to 1 afterwards does seemingly nothing. You can't see the difference in light output unless you have a delta of around 60 or 70. – GregoryNeal Sep 1 '18 at 16:05

For troubleshooting, I would take the received serial value out of the equation. Just set t = 0;

This is what I suspect may be happening.

This answer required too many assumptions because the received values were not explained. But it is a possibility if the MOSFET is working correctly.

I've got an HM10 BLE module connected to the arduino. I'm using the android app BLE scanner (for testing). Once I connect I'm sending a write request to the HM10's custom characteristic. The arduino is reading the bits and parsing the integer and then sending the data straight to the PWM pin.

If I understand correctly you are using a single serial ASCII character for your PWM values.

What you do not explain is the possible values that are received on the serial port.

A zero, in ASCII, would be a NULL character. Where a "0" would be 32, and 100 would be "d". Assuming you are using 7-character ASCII values and that being the reason you are using 0-100 dim level values.

That would make sense that you are actually sending a "0" (value of 32) rather than NULL (value zero). Your eye could easily misinterpret the difference between 32% and 60% luminous intensity.

I do not understand why the analogWrite(led, t); is inside the
if (Serial.available() > 0){}.
You could only send a single character to change the dim level if the
analogWrite(led, t);
was not within the if brackets.
I think you are unnecessarily sending a continuous stream of characters.

• I've revised my code and you can see it in my edit. In the BLE scanner app, I have an option to send text or byte arrays. I've been sending text values from 0 to 255, which I assume has been getting parsed correctly by arduino's Serial.parseInt function. Do arduino libraries not correctly parse the text value received from a ble device? – GregoryNeal Sep 3 '18 at 21:15
• When you receive an ASCII "0" the interger value is 32. When you print an integer with a value of 32 you will likely receive an ASCII "0". This can fool you into thinking the value of intR is zero when it is actually 32. I am not familiar with Arduino libraries. Typically with C, to send an ASCII zero character you can "print" 32 or "0". You are sending an integer value which the compiler may convert the integer to its ASCII character. I have not written any Atmel code in the past 20 years. I wrote mostly assembly and when I wrote in C, I used IAR Systems Embeded Workbwench – Misunderstood Sep 4 '18 at 19:51
• According to this arduino tutorial and my reference to the documentation, Serial.parseInt() reads the ASCII value from the stream buffer and parses it into the logical integer and not the ordinal value. If the buffer contained "5,220,70", then the first call to Serial.parseInt() will return the integer 5, the second call returns 220. In my updated code I'm not sending any comma separated values, so the single call to Serial.parseInt() should return the correct value. – GregoryNeal Sep 5 '18 at 1:44
• You should try taking analogWrite(led, intR); out of the if statement. analogWrite should be executed every iteration of the loop. You are only executing analogWrite when serial data is being received. I strongly recommend trying to just use a value of zero for intR and do not use the received data to set the value of intR or change the analogWrite to analogWrite(led,0); – Misunderstood Sep 5 '18 at 6:41