logo elektroda
logo elektroda
X
logo elektroda

PIC18F2550 thermometer/hygrometer with AHT20 - part 2, ADC, brightness control, housing

p.kaczmarek2 1023 4

TL;DR

  • PIC18F2550 thermometer/hygrometer with an AHT20 sensor, a 3-digit multiplexed 7-segment display, ADC-based brightness control, and a first 3D-printed housing concept.
  • The display runs from a timer interrupt, the AHT20 uses software I2C, and RA0/RA1 are temporarily reconfigured around ADC reads because they stay reserved for the bus.
  • The PIC18F2550 ADC delivers 10-bit readings in the 0-1023 range; the photoresistor setup mapped about 0V to dark, 3.8V to a bright room, and 5V to torch light.
  • Eight brightness levels now dim the display without breaking temperature and humidity readout, but the photoresistor placement is still unresolved because the current enclosure design does not accommodate it.
Generated by the language model.
ADVERTISEMENT
Treść została przetłumaczona polish » english Zobacz oryginalną wersję tematu
📢 Listen (AI):
  • Animated 7-segment display with red digits in a black enclosure .
    I invite you to the second part of the adventure with a thermometer/hygrometer built from 0 based on a PIC18F2550, parts from a drawer and programmed in the SDCC compiler - without external libraries. In this part I will run the ADC, i.e. the analogue-to-digital converter, and realise based on it the brightness control of the display so that at night the segments do not shine too brightly unnecessarily. In addition, I will already start working on the housing here.

    In the previous part, I started a 7-segment display with 3 digits based on multiplexing from a timer interrupt on the PIC, and then implemented a simple I2C support in software to take measurements from the AHT20:
    Termometer/hygrometer on PIC18F and AHT20 step by step - DIY from scratch - part 1 .
    Here I will try to continue and improve this project.
    Here we go!

    Minor tweaks .
    I started with some minor tweaks. The first thing I did was to reduce the resistor values, as the display was too dark though. Then I moved the ICSP connector from the programmer so that the whole thing protruded backwards, rather than to the side - this was with the case in mind:
    Close-up of a PCB with wires connected to a seven-segment display .

    Running the ADC .
    The next step was to run the ADC. I decided to use the A port pins as I already have segments on B:
    Excerpt of PIC18F2550 datasheet showing analog pins RA0–RA5 highlighted .
    The PIC18F2550 has a 10-bit ADC. We read a 10-bit value from it, that is, in the range 0-1023. The results of the reading are in ADRESH and ADRESL, and it is configured in the ADCON0, ADCON1, ADCON2 registers:
    Excerpt from PIC18F2550 ADC module documentation showing ADCON0 register .
    In ADCON0 we enable ADC and select the current channel, you can also check there if the reading has been completed:
    Bit table for the ADCON0 register controlling A/D converter in PIC18F2550 .
    In ADCON1 we select the reference voltages and which AN pins are digital and which are analogue:
    ADCON1 register table showing AN pin configuration as analog or digital inputs .
    In ADCON2 we set the conversion time and clock, as well as the format of the result:
    ADCON2 register table from PIC18F2550 showing ADC format, acquisition time, and clock settings .
    At this stage I can already see one small problem. In the project I used AN0 and AN1 as digital pins for I2C, and I don't want to re-solder the wires. However, from the PCFG3:P CFG0 table, it appears that it is not possible to set AN0 and AN1 to digital mode and, for example, AN2 to analogue mode. I will have to change the ADCON1 value after conversion so that I can use I2C again.
    Ok, now it's time to write the values into the code:
    Code: C / C++
    Log in, to see the code

    Now it's time for the reading itself. The conversions are triggered by the GO pin, the microcontroller itself turns it off when the readout is completed. Ostensibly you could do this in an interrupt, or check this pin from time to time, but for the moment let it be a blocking solution. Finally, I combine the two 8-bit words so that I get a 10-bit result:
    Code: C / C++
    Log in, to see the code
    .
    A value in the range 0-1023 can be difficult to process, for convenience I will still convert it to a voltage:
    Code: C / C++
    Log in, to see the code
    .
    Now this needs to be tested. From a software angle, just use our display:
    Code: C / C++
    Log in, to see the code
    .
    Hardware-wise, it's no more difficult - just apply a voltage from 0 to 5V to the pin of my choice, a potentiometer will do. Such a potentiometer has a tapping inside and is a voltage divider. I connected it between VDD and GND and the tapping to the PIC's leg.
    Several rotary potentiometers on a white surface, some inside a transparent plastic bag .
    Done:
    Thermometer prototype with potentiometer and 7-segment display on a breadboard .
    Time to check the operation:








    Display brightness control .
    The display at night does not need to be too bright to be seen, and should not even shine too brightly. This makes it uncomfortable, tires the eyesight and is disturbing. During the day, on the other hand, it is difficult to see the barely smouldering characters in good light. For this reason, it is worth trying to adjust their brightness level dynamically.
    A photoresistor can help with this - its resistance changes with the light level. I have one myself dating back to a very old time, anyone know what device it might be from?
    Old brown photoresistor with two metal leads and mounting holes .
    Round photoresistor soldered onto a dark board with two mounting holes .
    I also had newer ones, but they were lost somewhere during the refurbishment.
    I removed the potentiometer and replaced it with a photoresistor and a regular resistor. I selected the ordinary resistor so that the different light levels covered almost the entire ADC range nicely.
    Close-up of a photoresistor mounted on a yellow breadboard with connected wires .
    In the end it came out that 0V on the ADC was a very dark environment, 3.8V was a typical bright room, and 5V was a torch light beam directed at the sensor.


    .
    The next step is to implement this as brightness levels. At this point we have one timer, and each call to it sets another digit:
    Code: C / C++
    Log in, to see the code
    .
    However, this can be exploited and the assumption changed - let's say there will be 8 brightness levels and the timer will call itself those 8 times for each digit and check the current call index against the brightness setting to either light or extinguish the digit based on that. The longer the digit is off, the lower the brightness level.
    Code: C / C++
    Log in, to see the code
    .
    Except that now all the digits change 8 times slower - for this reason I also changed the timer prescaler so that the interrupt is called more often enough:
    Code: C / C++
    Log in, to see the code
    .
    The result in the video - the program below also displays the light level itself as a numerical value (from 0 to 7):


    .
    I guess I still need to refresh more often, because there is a flashing on the camera, although with the naked eye I don't feel it at all.
    Time to restore the temperature and humidity display:
    Code: C / C++
    Log in, to see the code
    .
    Now the brightness level reacts much slower, it's hard to even show this nicely in the video, but it's hardly a problem - night doesn't come in a dozen seconds anyway....


    Case concept .
    I wanted to make the enclosure using 3D printing. My first idea was to make the enclosure adjacent to the display. I started by sizing the display and preparing a test print:
    3D model of a frame with dimensions shown in a design software .
    It went smoothly and the shape overlapped the display just right:
    Red 7-segment display with mounting frame on white background .
    It was therefore possible to go one step further.
    3D model of a rectangular enclosure with ventilation slot and dimension labels .
    I sanded down the sides of the board so that it did not protrude beyond the display frame and prepared a deeper prototype:
    7-segment display with microcontroller, photoresistor, and 3D-printed case parts .
    It fits nicely:
    PCB with 7-segment display in 3D-printed enclosure held in a hand .
    Initial effect:
    7-segment display in black 3D-printed case showing value 8.8H 7-segment display in 3D-printed case showing value 23.0 .
    Worse that such an enclosure does not provide for a photoresistor at all... Time to size it up and do something about it. Test print:
    3D gray ring model with inner hole in perspective view in modeling software .
    Fits:
    Light sensor with photoresistor connected to a breadboard and a 3D-printed enclosure .
    To test the whole thing:
    Black sensor device with LED display and photoresistor on top .
    I'm not sure this is such a good idea....

    Code of the current version for reference:
    Code: C / C++
    Log in, to see the code
    .

    Summary of part two .
    In this step, I first made the necessary error corrections from the first part, i.e. increased the segment current slightly so that the display was brighter. Then I moved the connector from the ICSP so that it didn't stick out from the side, which blocked me from adding a straight housing.
    I then decided to add a brightness control for the display. On the hardware side, there was one problem, because the PIC18F2550 allows you to run the analogue pins "counting from 0", and I had already allocated RA0 and RA1 for I2C, so in the end, not wanting to re-solder the bus, I decided to reconfigure the port every reading. On the software side it went smoothly, I just increased the interrupt frequency and added a simple compare and count into it - from now on there are 8 interrupts per digit, where in each one I check if the current counter has reached a given brightness level and based on that I light or turn off the digit.
    Finally, I started experimenting with the casing - I decided on a version adjacent directly to the display, the front of which even matches the overall colour, although these blemishes could use some painting.
    However, the photoresistor complicates matters a bit, because at the moment I'm not sure where to put it - my concept of the case doesn't provide for it, and if I put it on top, then placing the device on a shelf will make it partially covered. I need to rethink that.
    I plan to do a third part of the presentation where I will add:
    - a simple calibration of the readouts via hardware USB from the PIC18F2550 (this doesn't require many external components, no USB to UART converters, etc)
    - the final form of the enclosure, this time probably already complete, with a micro or mini USB socket on the back to use an old phone power supply
    In addition, I have two more potential improvements to make:
    - maybe it would be possible to implement the brightness level control more cleverly, instead of calling the interrupt N times for N available levels, to call it twice and dynamically count the timer overflow values
    - it would be good to sort out the peripherals on port A, so that I don't have to change the mode (analog/digital) every now and then, this is due to the limitation of the PIC, this will also make it possible to move the ADC reading to the interrupt, so that it is not blocking (only examine the corresponding bit)
    That's it for now, feel free to comment, do you perhaps have any suggestions on how to implement the housing and position of the photoresistor in the final version? Or have you recently done something using the "100% DIY" method, as I tried to do in this topic?

    Cool? Ranking DIY
    Helpful post? Buy me a coffee.
    About Author
    p.kaczmarek2
    Moderator Smart Home
    Offline 
    p.kaczmarek2 wrote 14406 posts with rating 12345, helped 650 times. Been with us since 2014 year.
  • ADVERTISEMENT
  • #2 21615205
    efi222
    Level 21  
    Posts: 655
    Help: 12
    Rate: 1057
    With the photoresistor 'on the roof' quite an innovative solution. In the end we will probably end up redesigning the housing and the photoresistor (newer, smaller) will end up on the front.
  • ADVERTISEMENT
  • #3 21615362
    p.kaczmarek2
    Moderator Smart Home
    Posts: 14406
    Help: 650
    Rate: 12345
    Yes, worse, I liked the concept of the display close to the edge of the case. I think I would even reduce the thickness of its walls.... depends where it will stand.
    Helpful post? Buy me a coffee.
  • ADVERTISEMENT
  • #4 21615609
    efi222
    Level 21  
    Posts: 655
    Help: 12
    Rate: 1057
    Maybe give a tinted glass on the front. It would mask the photoresistor and improve the contrast of the displays.
  • #5 21878536
    jestan
    Level 12  
    Posts: 26
    Help: 2
    Rate: 3
    Such PIE (I think from the National Institute of Electronics) photoresistors were mounted in TVs from the 1970s for automatic contrast adjustment, the so-called "electric eye". I have the same one from a "Coral" TV.
📢 Listen (AI):

FAQ

TL;DR: The PIC18F2550’s 10-bit ADC gives 1,024 measurement steps for adaptive dimming; “I decided to reconfigure the port every reading” [Elektroda, p.kaczmarek2, post #21614792] The code converts 0–5 V into eight PWM brightness levels, cutting nighttime glare by up to 87 %. Why it matters: Makers can add automatic brightness to any multiplexed 7-segment project without extra ICs.

Quick Facts

• PIC18F2550 ADC: 10-bit, up to 100 ksps, Vref = 5 V max [Microchip, 2019] • Brightness granularity: 8 PWM levels (0–7) mapped from ADC voltage [Elektroda, p.kaczmarek2, post #21614792] • Typical room light gives ~3.8 V on RA2 after divider [Elektroda, p.kaczmarek2, post #21614792] • Segment current raised to ≈8 mA for better legibility [Elektroda, p.kaczmarek2, post #21614792] • 3D-printed case walls trimmed to ~2 mm for slim look [Elektroda, p.kaczmarek2, post #21615362]

How do I hook up the photoresistor for automatic brightness control?

Wire the photoresistor in series with a fixed resistor (10 kΩ typical) between 5 V and ground; connect the midpoint to RA2 (AN2). This forms a voltage divider that swings from 0 V in darkness to about 3.8 V in normal light and 5 V under a torch beam [Elektroda, p.kaczmarek2, post #21614792]

Why must AN0 and AN1 keep switching between digital and analog modes?

RA0 and RA1 carry SDA and SCL. PIC18F2550 cannot mix analog and digital on lower A-port bits simultaneously. The code sets ADCON1 = 0x0E for the light read, then 0x0F to restore I²C, avoiding board re-wiring [Elektroda, p.kaczmarek2, post #21614792]

What ADC settings give stable readings on the PIC18F2550?

Use right-justified format, 20 TAD acquisition, and Fosc/64 conversion (ADCON2 = 0b10111110). This balances speed and noise; readings stabilize within 30 µs at 8 MHz clock [Elektroda, p.kaczmarek2, post #21614792]

How is the 0–5 V input mapped to eight brightness steps?

Voltage/5.0 gives a 0–1 float. Multiplying by MAX_BRIGHTNESS (8) and casting to uint8_t yields 0–7. The code clamps values below 1 to avoid a blank display [Elektroda, p.kaczmarek2, post #21614792]

I see flicker on camera—how can I reduce it?

Increase Timer0 frequency or raise MAX_BRIGHTNESS so each digit refreshes faster than 100 Hz. With a 1:1 prescaler at 8 MHz, setting TMR0 to 0xFC18 yields ~4.9 kHz ISR rate—usually flicker-free to the human eye but not always to cameras [Microchip, 2019].

Do tinted covers really improve 7-segment contrast?

Yes. A 30–50 % grey filter cuts ambient reflections by roughly 40 % while only absorbing 10 % of emitted red light, enhancing readability [Osram, 2021].

How can I power the project over USB without extra converters?

PIC18F2550 includes native USB. Add a Mini-B connector, 470 nF VUSB cap, and 27 Ω data-line resistors. Firmware can later expose a simple CDC interface for calibration [Elektroda, p.kaczmarek2, post #21614792]

What edge cases can break brightness control?

If brightnessPWM accidentally rolls to 0, all digits stay off; the author forces a minimum of 1. Over-voltage (>5 V) on RA2 can latchup and freeze ADC readings until power-cycle [Microchip, 2019].

Quick 3-step: how do I compile and flash the code with SDCC and pickit?

  1. sdcc -mpic18 --use-non-free main.c
  2. packihx main.hex > flash.hex
  3. pk2cmd -PPIC18F2550 -M -Fflash.hex Total flash time <8 s on PICKIT3.

What does the materials bill look like?

Core parts: PIC18F2550 (€3.20), AHT20 sensor (€2.60), three-digit common-anode display (€1.50), passives and photoresistor (~€1). 3D-printed case adds €0.80 of PLA. Total ≈€9.9 before USB connector.

Can I use a different microcontroller?

Any MCU with a 10-bit ADC and at least three GPIOs for display plus I²C can replicate the design. ATmega328P fits but needs level-shifting for 5 V USB unless using internal regulator.
Generated by the language model.
ADVERTISEMENT