šŸ“Š Bargraph šŸ”¢ Voltmeter šŸ•Æļø Light Meter – PICAXE 08M2+ MAX7219

radiosparks

Well-known member
šŸ“Š Bargraph šŸ”¢ VoltmeterThis was a project to characterize ALS (Ambient Light Sensors). I could’ve used my trusty AVO meter and a resistor, but figured it was a good excuse to play with a PICAXE. I wasn’t chasing accuracy by using Vcc as a reference—just a relative response from my stash of light sensors.

I read @westaust55’s excellent PDF: ā€œEverything you ever wanted to know about the MAX7219, but were afraid to askā€
Link: Getting Started with the MAX7219

The sample code worked. I noticed it only used 4 digits, but my display had 8. A second read confirmed each digit can be set independently—interesting! The TINKER coder in me woke up, and this is what I built. šŸ˜Ž

I’ve stripped out extra code to simplify reuse. You can add your own command-based modes.
Modes 0–5 cycle through different bargraph styles. Other modes that I’ve used in the matrix display version:
The Push-button Trick!
Mode 6: Peak-hold
Mode 7: Scrolling trace
Mode 8: Flash-on-change
Mode 9: Ambient histogram

šŸ’” The Push-button Trick!

Instead of using C.3 as a digital input (reserved for future IR control), I used a neat ADC trick. ALS devices are usually NPN transistors with a Vce(sat) around 0.7V, so they never reach full Vcc in the Emitter follower circuit. That means the ADC maxes out around 4.3V when using a 5 Volt supply.

The push-button shorts the ALS to Vcc, giving a full-scale ADC reading. Handy when you want a button
and analog input on the same pin. I use this trick often with pots too.

NPN photo Darlington
🧠 What’s an ALS?

Ambient Light Sensors mimic the human eye’s response to brightness. This one’s a sensitive NPN photo Darlington in a 3mm T-1 package—RoHS-friendly (no CdS). Its clear case makes it IR-sensitive, so it reacts to incandescent bulbs and sunlight. Even can detect pulses from IR remote controls.

It’s fast enough to detect AC LED lamp flicker. To smooth the output, I use a ring buffer: 10 samples are averaged and scaled to show a voltage across the resistor.

Component: Everlight ALS-PDT144-6C/L451
Technology: Photo-darlington transistor optimized for Human eye light response.
Datasheet Link: ALS-PDT144-6C-L451_datasheet_V4.pdf
Application Note: Analog ALS Application Note V2.0

šŸ“ Circuit Notes

I’ve included reference connection styles for ALS. Don’t take the values as gospel—you’ll need to tune them for your setup. For example, the resistor value sets the sensitivity of the ALS and should be adjusted based on your specific conditions.
ALS reference connections
There’s some circuit loading due to the low input impedance of the ADC (around 10 kĪ©), so readings tend to be lower than those you'd get using a digital voltmeter (DVM) for measurement.

🧪 What’s Next?

Glad you asked. In the next post, I use a MAX7219 with a 64-LED (8Ɨ8) matrix display to create a trend line based light meter.

"Show me the CODE!"
Please see the next following post for code and circuit layput.

PICAXE_08M2_MAX7219_Lightmeter_Voltmeter_Bargrapg.jpg
 
Last edited:
PEBBLE4 Circuit: See attachment below to import circuit details

PEBBLE_BreadBoard_Capture(1).jpg

CODE: See attachment for importing to PICAXE Editor

Code:
#rem

    ---[ PICAXE 08M2 MAX7219 Bargraph / Voltmeter / Lightmeter Project (v3b) ]---

    Author: rhb.20251020 (aka. radiosparks)

#endrem

' ---[ Compiler Directives ]--------------------------------

#picaxe 08M2    ; My favorite PICAXE, only chip I can lay my hands on ... HI ... rhb.
setfreq M16     ; My favorite speed - doesn't change the timing
REM #no_data    ; remove REM after running once to edit code faster

' ---[ MCU PORT Connections ]-------------------------------

    '// MAX7219 - Control signal lines from PICAXE to MAX7219

    symbol DIn_7219     = pinC.0
    symbol Load_7219    = C.1
    symbol Clk_7219     = C.2

    '// Photo Sensor Input

    symbol ALS          = C.4
    symbol IRpin        = C.3 ; reserved future IR remote control use

' ---[ Constants ]------------------------------------------

    '// MAX7219 Control Register Numbers

    symbol No_Op         = 0

    symbol Digit1        = 1   ; Digit 1 UNITS    for 7-SEG numerical display
    symbol Digit2        = 2   ; Digit 2 TENS     for 7-SEG numerical display
    symbol Digit3        = 3   ; Digit 3 HUNDREDS for 7-SEG numerical display
    symbol Digit4        = 4   ; Digits 4 MSB to 8 for bargraph display
    symbol Digit5        = 5
    symbol Digit6        = 6
    symbol Digit7        = 7
    symbol Digit8        = 8   ; Digit 8 LSB for bargraph display

    symbol Decode        = 9   ;
    symbol Intensity     = 10  ;
    symbol ScanLimit     = 11  ;
    symbol ShutDown      = 12  ;
    symbol DisplayTest   = 15  ;

    '// MAX7219 Default Values

    symbol Set_Off       = 0         ; FALSE
    symbol Set_On        = 1         ; TRUE
    symbol No_Digits     = 7         ; Scan limit all digits 1 to 8
    
    symbol Dec_Digits   = $07       ; Decode digits 1 to 3 according to "code B"
                                    ; No Decode for digits 4 to 8 bargraph
                                    
    symbol Init_Inten   = 0            ; 0 lowest dim (=1/32 PWM on time) to 15 (=31/32 PWM on time) very very bright
    symbol DigBlank     = $0F       ; MAX7219 "Code B" value to blank a digit
    symbol DecimalPoint = $80       ; OR this value to a digit value/code when we want the decimal point illuminated
    symbol Base         = 10        ; constant for base conversion

    '// Bargraph Ring/Circular Buffer

    symbol BufferSize   = 10        ; Sets the length of the Ring/Circular Buffer
    symbol BufferStart  = 30        ; RAM Buffer start pointer
    symbol BufferLimit  = BufferStart + BufferSize
    symbol BufferEnd    = BufferLimit - 1

' ---[ Variables ]------------------------------------------

    '// BIT Registers
    symbol Data_7219    = b0  ; Data Register for MAX7219
    symbol Register     = b1  ; Command Register for MAX7219
    symbol spareBITS2   = b2  ; Any Register with SPARE is "free and clear" to use
    symbol spareBITS3   = b3

    '// BYTE Registers
    symbol i            = b4  ; temporary reusable registers
    symbol j            = b5  ; ^
    symbol k            = b6  ; ^
    
    symbol barCount     = b7  ; bargraph length
    symbol barSEG1      = b8  ; bargraph half segment pattern
    symbol barSEG2      = b9  ; bargraph full and half segment pattern
    symbol Mode         = b10 ; bargraph display Mode
    symbol spareb11     = b11
    symbol spareb12     = b12
    symbol IR_input     = b13 ; reserved future IR remote control use
        
    '// Ring/Circular Buffer Variables
    symbol latest_data  = b14 ; ADC Input Data
    symbol RAM_pointer  = b15 ; Buffer pointer in RAM
    symbol samples      = b16 ; samples counter
    symbol spareb17     = b17
    symbol spareb18     = b18
    symbol spareb19     = b19

    '// WORD Registers
    symbol average      = w10 ; b21:b20
    symbol prevAverage  = w11 ; b23:b22
    symbol spareW12     = w12 ; b25:b24
    symbol spareW13     = w13 ; b27:b26

    '// System Registers - Only for 08M2+ - used only for an example
    symbol timeout      = s_w1
    symbol spare_s_w2   = s_w2
    symbol spare_s_w3   = s_w3
    symbol spare_s_w4   = s_w4
    symbol spare_s_w5   = s_w5
    symbol spare_s_w6   = s_w6

' ---[ Program Start ]--------------------------------------

' ---[ Initialization ]-------------------------------------

Initialise7219:

    ' ---[ Initialise the MAX7219 7-Seg LED display driver ]---
    
    LOW Load_7219
    LOW Clk_7219
    DIn_7219 = 0

    Register = Intensity   : Data_7219 = Init_Inten : gosub ShiftTo7219
    Register = ScanLimit   : Data_7219 = No_Digits  : gosub ShiftTo7219
    Register = DisplayTest : Data_7219 = Set_Off    : gosub ShiftTo7219
    Register = Decode      : Data_7219 = Dec_Digits : gosub ShiftTo7219
    
    '// Clear the MAX7219 Display RAM to prevent a random LED display
    for i = 0 to 7 : Register = Digit1 + i : Data_7219 = 0 : gosub ShiftTo7219 : next i

    Register = ShutDown    : Data_7219 = Set_On     : gosub ShiftTo7219
    
    '// Bargraph Defaults
    
        Mode = 0
     barSEG1 = $04
     barSEG2 = $34
    
' ---[ Program Code ]---------------------------------------

Main:

    do       
        '// Get a reading from the ADC
        readadc ALS, latest_data
    
TRick_Button_Input:

        if latest_data > 250 then
            timeout = 0 ; start a timeout
            Do
                Pause 100
                timeout = timeout + 1 ; 08M2 Example using system word variables
                readadc ALS, latest_data
                If latest_data < 250 Then
                    ' ---[ Command Selector ]---
                    Mode = Mode + 1 // 6 ; keep system variable in bounds ( 0 to 5 )
                        read Mode, barSEG1
                        k = Mode + 6
                        read k, barSEG2
                    exit
                End If
            Loop Until timeout >= 2000
        end if

Add_Data_To_Buffer:

    '//stuff ADC data into ring/circular Buffer : @hippy
    
           bPtr = bPtr + 1 // BufferLimit Min BufferStart
          @bPtr = latest_data
        samples = samples + 1 Max BufferSize

Calculate_Average:

        prevAverage = average       ; To determine the movement direction of the bargraph
            average = 0
      
        for RAM_pointer = BufferStart To BufferEnd
            Peek RAM_pointer, latest_data
            average = average + latest_Data
        next RAM_pointer
    
        'average = average / samples    ; raw average calculation
        
        ' NOTE: If you change the BufferSize you'll need to adjust the average to voltage ratio 10/51
        average = average * samples / 51     ; convert average to a voltage reading 2 decimal places

Calculate_Digits:

        ' ---[ Calculate (Extract) the individual digits for the current value and send to MAX7219 ]---
        '// turning on decimal point forces leading zero 0.00
        
        Register = Digit3 : Data_7219 = average / Base / Base | DecimalPoint  ; Hundreds
        gosub ShiftTo7219
        Register = Digit2 : Data_7219 = average / Base // Base                ; Tens
        gosub ShiftTo7219
        Register = Digit1 : Data_7219 = average // Base                       ; Units
        gosub ShiftTo7219

BarGraph_Display:

        barCount = average / 50    ; bargraph step index (0 to 10)

        if prevAverage < average then   
            ; smooth sweep up bargraph
            for i = 0 to 4 : gosub SetBarGraph : next i
        else                       
            ; smooth sweep down bargraph
            for i = 4 to 0 step -1 : gosub SetBarGraph : next i
        endif
        
    loop

end

' ---[ Subroutines ]----------------------------------------

SetBarGraph:

    ' ---[ Bargraph Segment Routine ]---

    Data_7219 = $00                 ; default clear segments
    k = 2 * i + 1                   ; half segment index : 1, 3, 5, 7, 9
    
    if barCount > k then
        Data_7219 = barSEG2         ; full and half segment
    elseif barCount = k then
        Data_7219 = barSEG1         ; half segment
    endif
    
    Register = Digit8 - i : gosub ShiftTo7219
    pause 50 ; provides a little damping of the bargraph movement

return

ShiftTo7219:

    #rem
      Subroutine to shift the register address and data value out to the MAX7219
      The MAX7219 incoming data is clocked in through the lsb towards the msb.
      if only one MAX7219 is conencted then the first four significant bits of data
      can be ignored to improve the data transfer speed by 25%
      @westaust55
    #endrem
    
    '// Register b1
    ' * enable if two or more MAX7219 are cascaded together
    
;    PULSOUT Clk_7219, 1 ; bit 15 is don't care
;    PULSOUT Clk_7219, 1 ; bit 14 is don't care
;    PULSOUT Clk_7219, 1 ; bit 13 is don't care
;    PULSOUT Clk_7219, 1 ; bit 12 is don't care
    
    DIn_7219 = bit11 : PULSOUT Clk_7219, 1
    DIn_7219 = bit10 : PULSOUT Clk_7219, 1
    DIn_7219 = bit9  : PULSOUT Clk_7219, 1
    DIn_7219 = bit8  : PULSOUT Clk_7219, 1
    
    '// Data_7219 b0
    
    DIn_7219 = bit7  : PULSOUT Clk_7219, 1
    DIn_7219 = bit6  : PULSOUT Clk_7219, 1
    DIn_7219 = bit5  : PULSOUT Clk_7219, 1
    DIn_7219 = bit4  : PULSOUT Clk_7219, 1
    DIn_7219 = bit3  : PULSOUT Clk_7219, 1
    DIn_7219 = bit2  : PULSOUT Clk_7219, 1
    DIn_7219 = bit1  : PULSOUT Clk_7219, 1
    DIn_7219 = bit0  : PULSOUT Clk_7219, 1
    
    '// pulse the LOAD/CS pin on MAX7219 to latch the data into the RAM array
    
    PULSOUT Load_7219, 1   
    
return

' ---[ EEPROM Bargraph Segment Data ]---

    data 0, ($04, $04, $06, $02, $02, $04) ; barSEG1
    data 6, ($34, $14, $36, $32, $22, $24) ; barSEG2

'--[eof]---
 

Attachments

Last edited:

šŸ“‰ Trend Line šŸ“Š Bargraph šŸ•Æļø Light Meter and 🧻 Scrolling 3X5 Pixel Text​


Using PICAXE 08M2 MAX7219 8X8 Matrix Display - V3b

My original idea was to measure the light over time to make sure my tropical plants got enough. Then I planned to add a supplemental light source to cover the lower levels during winter. But, as is often the case, the project went down another rabbit hole and ended up here.

The light level scrolls only when it changes. Adding the font array for scrolling text messages was a natural step.

This demo runs off a 5V USB power bank through my homemade FTDI programming cable. I used the cap from a spray bottle with a curl of paper inside to diffuse the light to the ALS (Ambient Light Sensor), since those sensors tend to be pretty directional. HINT: Just swap out the ALS and use a potentiometer to measure its position.

To improve the video, I placed a red filter over the LED matrix.


PEBBLE4 Circuit: Same circuit, just a change to the 8x8 matrix display.

PEBBLE_4_PICAXE_08M2_MAX7219_Trend_Line_Bargraph_Light_Meter.jpg

Hit the 10K post cap again! Hopefully REV-ED will extend the limit for verified users who post regularly. For those that want to see the code, un-folded, visit my site: http://radiosparks.com/projects.php?PID=44 šŸŒŽ

Added an EXCEL sheet I used to edit the MicroFONT 3X5 pixel code.
 

Attachments

Back
Top