############################################################### #SWR-meter v0.3 15.07.2026 ############################################################### # # from machine import Pin, I2C from ssd1306 import SSD1306_I2C import framebuf,sys,time from writer import Writer import freesans20 pix_res_x = 128 # SSD1306 horizontal resolution pix_res_y = 64 # SSD1306 vertical resolution i2c_dev = I2C(0,scl=Pin(21),sda=Pin(20),freq=200000) # start I2C on I2C1 (GPIO 26/27) i2c_addr = [hex(ii) for ii in i2c_dev.scan()] # get I2C address in hex format if i2c_addr==[]: print('No I2C Display Found') sys.exit() # exit routine if no dev found else: print("I2C Address : {}".format(i2c_addr[0])) # I2C device address print("I2C Configuration: {}".format(i2c_dev)) # print I2C params # --- Hardware Register Constants --- ADC_BASE = const(0x4004C000) # CS Register Offsets: EN=bit 0, START_MANY=bit 3, READY=bit 8, AINSEL=bits 12-14 ADC_CS = const(ADC_BASE + 0x00) ADC_RES = const(ADC_BASE + 0x04) # Result register offset # 1. Initialize the pins for analog function using the standard framework # ADC0 is GPIO 26, ADC1 is GPIO 27 pin26 = machine.Pin(26, machine.Pin.IN) pin27 = machine.Pin(27, machine.Pin.IN) @micropython.viper def init_adc_hardware(): """Enable the internal ADC peripheral clock and power.""" ptr32(ADC_CS)[0] = 1 # Set EN (bit 0) to 1 to enable the ADC @micropython.viper def read_adc_fast(channel: int) -> int: """Reads a 12-bit raw value from a specific RP2040 ADC channel. channel: 0 for ADC0 (GPIO 26), 1 for ADC1 (GPIO 27) """ p_cs = ptr32(ADC_CS) p_res = ptr32(ADC_RES) # 1. Select the analog channel and keep ADC enabled # AINSEL starts at bit 12. (channel << 12) | 1 (EN bit) p_cs[0] = (channel << 12) | 1 # 2. Start a single conversion by setting START_ONCE (bit 2) p_cs[0] = (channel << 12) | 5 # 3. Poll the READY status bit (bit 8) until it goes high (1) while not (p_cs[0] & 0x100): pass # 4. Extract and return the raw 12-bit result (0 - 4095) return p_res[0] & 0xFFF init_adc_hardware() oled = SSD1306_I2C(pix_res_x, pix_res_y, i2c_dev) # oled controller wri = Writer(oled, freesans20) oled.write_cmd(0xc8) # flip display to place 0,0 at lower-left corner oled.write_cmd(0xA1) def get_swr(valfwd, valref): # 1. Handle No Transmission: If forward voltage is zero or near zero noise # (e.g., less than 0.05V), the radio is not transmitting. if valfwd <= 62: return 0.0 # Or return None / "---" to show the transmitter is idle # 2. Handle Hardware Errors: Reflected voltage cannot exceed forward voltage. # If noise makes ref > fwd, the denominator becomes negative. if valref >= valfwd: return 99.9 # Return a high value to signal a severe mismatch/error # 3. Calculate standard SWR swr = (valfwd + valref) / (valfwd - valref) # 4. Final safety check for impossible math results if swr < 1.0: return 1.0 return swr while True: valfwd = read_adc_fast(1) # Read Channel 0 valref = read_adc_fast(0) # Read Channel 1 v_valfwd=(((valfwd/4095)*3.3)+0.35)*11 v_valref=(((valref/4095)*3.3)+0.35)*11 p_valfwd=(v_valfwd**2)/100 p_valref=(v_valref**2)/100 realfwd=(-0.00463584*p_valfwd**3)+(0.07561205*p_valfwd**2)+(1.57173924*p_valfwd)-0.61109231 realref=(-0.00463584*p_valref**3)+(0.07561205*p_valref**2)+(1.57173924*p_valref)-0.61109231 oled.fill(0) # clear the display if realfwd <=0.25 : wri.set_textpos(oled, 0, 10) wri.printstring("FWD: "+str(round(0.0,1))+" W") else: wri.set_textpos(oled, 0, 10) wri.printstring("FWD: "+str(round(realfwd,1))+" W") if realref <=0.25 : wri.set_textpos(oled, 20, 10) wri.printstring("REF : "+str(round(0.0,1))+" W") else: wri.set_textpos(oled, 20, 10) wri.printstring("REF : "+str(round(realref,1))+" W") swr=get_swr(valfwd,valref) wri.set_textpos(oled, 40, 10) wri.printstring("SWR: "+str(round(swr,1))) if valfwd>4092 or valref>4092: oled.fill(0) wri.set_textpos(oled, 20, 8) wri.printstring("OVERLOAD") oled.show() # show the new text and image time.sleep(0.2)