/* ////////////////////////////////////////////////////////////////// ARDUINO/Genuino Ptoject "Wanmod", a HF Measurement Receiver https://www.changpuak.ch/electronics/Arduino-Wanmod.php Software Version 9.9 26.12.2022 by ALEXANDER SSE FRANK The Serial Parser is from here : Easy_Diseqc V1.2, Monstein ETH Zurich, 20.06.2018 ////////////////////////////////////////////////////////////////// */ #include #include #include // CONNECTIONS OLED #define OLED_MOSI 11 #define OLED_CLK 13 #define OLED_DC 9 #define OLED_CS 10 #define OLED_RESET 8 // ROTARY ENCODER VARIABLES const int RE2 = 2 ; // ROTATION const int RE3 = 3 ; // ROTATION const int RE4 = 4 ; // PRESSED byte CursorPos = 3 ; bool LEFT = false ; bool RIGHT = false ; bool PRESS = false ; bool READY = true ; char tempbuf[80]; // keeps the command temporary until CRLF String buffer; Adafruit_SH1106 display(OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS); #if (SH1106_LCDHEIGHT != 64) #error("Height incorrect, please fix Adafruit_SH1106.h!"); #endif int MAX_BAR_LENGTH = 127 ; int MIN_BAR_LENGTH = 10 ; // FREQUENCY float F_RX = 10.000 ; // DEFAULT FREQUENCY const float F_IF1 = 110.592 ; // FIRST IF FREQUENCY const float F_IF2 = 10.7 ; // SECOND IF FREQUENCY const float F_IFT = 110.7 ; // TRUE IF FREQUENCY const float F_RX_MAX = 32.768 ; // MAXIMUM RX FREQUENCY const float F_RX_MIN = 0.030 ; // MINIMUM RX FREQUENCY // RSSI float RSSI = -99.9 ; const float RSSImin = -99.9 ; const float RSSImax = -10.0 ; const int RSSI_CLK = A0 ; const int RSSI_DAT = A1 ; const int RSSI_CS = A2 ; // SUPPLY MONITOR const int SupplyPin = A7 ; // INPUT unsigned int SupplyRaw = 0 ; float SupplyFactor = 0.006 ; // 1.5 kR : ( 1.5 kR + 7.5 kR ) float SupplyVoltage = 0.0 ; const float Diode = 0.65 ; bool debug = false ; // ///////////////////////////////////////////////////////////////////// // SUBROUTINES DISPLAY. // ///////////////////////////////////////////////////////////////////// int timer = 0 ; void UpDateDisplay() { display.clearDisplay(); display.drawLine(0, 12, 128, 12, WHITE); display.setTextColor(WHITE) ; display.setTextSize(1) ; display.setTextColor(WHITE) ; // FREQUENCY char buff[32]; dtostrf(F_RX, 6, 3, buff); display.setTextSize(2) ; display.setCursor(4,20) ; display.print(buff[0]) ; display.setCursor(18,20) ; display.print(buff[1]) ; display.setCursor(29,20) ; display.print(buff[2]) ; display.setCursor(42,20) ; display.print(buff[3]) ; display.setCursor(56,20) ; display.print(buff[4]) ; display.setCursor(70,20) ; display.print(buff[5]) ; display.setCursor(92,20) ; display.print("MHz") ; // SET CURSOR switch(CursorPos) { case 0: display.setCursor(4,25) ; display.print("_") ; break; case 1: display.setCursor(18,25) ; display.print("_") ; break; case 2: display.setCursor(42,25) ; display.print("_") ; break; case 3: display.setCursor(56,25) ; display.print("_") ; break; case 4: display.setCursor(70,25) ; display.print("_") ; break; } // DRAW TICKS RSSI BAR display.setTextSize(1) ; display.setCursor(0,35) ; for (int i=0; i<=6; i++) { display.drawLine(i*20, 54, i*20, 58, WHITE) ; } for (int i=0; i<=25; i++) { display.drawLine(i*5, 56, i*5, 58, WHITE) ; } display.setTextSize(1) ; display.setCursor(29,44) ; display.print("-80") ; display.setCursor(69,44) ; display.print("-40") ; display.setCursor(112,44) ; display.print(" 0") ; BAR(RSSI) ; // PRINT RSSI VALUE IN DA TOP RIGHT CORNER display.setCursor(67,1) ; if((RSSI > -100.0) && (RSSI <= -9.95)) display.print(" ") ; if((RSSI > -9.95) && (RSSI < 0.0)) display.print(" ") ; if((RSSI > -0.05) && (RSSI < 0.1)) display.print(" ") ; if((RSSI > 0.05) && (RSSI < 10.0)) display.print(" +") ; if((RSSI > 10.0) && (RSSI < 29.9)) display.print(" +") ; if(RSSI < -99.9) display.print(" -99.9") ; else display.print(RSSI,1) ; display.print(" dBm") ; // BATTERY VOLTAGE UpdateBattVolt() ; display.setCursor(0,1) ; if(SupplyVoltage < 10.0) display.print(" ") ; display.print(SupplyVoltage,1) ; display.print(" V") ; display.display() ; } void UpdateBattVolt() { // We use 10 Bits here SupplyRaw = analogRead(SupplyPin) ; SupplyVoltage = SupplyFactor * map(SupplyRaw, 0, 1023, 0, 5000) ; SupplyVoltage += Diode ; display.drawRect(40, 1, 18, 7, WHITE); display.drawRect(58, 2, 2, 5, WHITE); display.fillRect(57, 3, 2, 3, BLACK); if(SupplyVoltage >= 9.0) display.fillRect(42, 3, 2, 3, WHITE); if(SupplyVoltage >= 11.0) display.fillRect(45, 3, 2, 3, WHITE); if(SupplyVoltage >= 13.0) display.fillRect(48, 3, 2, 3, WHITE); if(SupplyVoltage >= 15.0) display.fillRect(51, 3, 2, 3, WHITE); if(SupplyVoltage >= 17.0) display.fillRect(54, 3, 2, 3, WHITE); // Serial.println(SupplyRaw, DEC); } void BAR(float value) { int BAR_LENGTH = (int)(120.0 + value) ; if (BAR_LENGTH > MAX_BAR_LENGTH) BAR_LENGTH = MAX_BAR_LENGTH ; if (BAR_LENGTH < MIN_BAR_LENGTH) BAR_LENGTH = MIN_BAR_LENGTH ; display.fillRect(0, 57, BAR_LENGTH, 6, WHITE); display.fillRect(BAR_LENGTH, 58, MAX_BAR_LENGTH-BAR_LENGTH, 4, BLACK); display.drawRect(0, 57, 127, 6, WHITE); } void UpdateRSSI() { // WE USE A MAX1284 HERE // Serial 12-Bit ADCs with Internal Reference // 0.0 V ==> 0x000, 2.5 V ==> 0xFFF unsigned int RAW_LO = 0 ; unsigned int RAW_HI = 0 ; unsigned int RAW = 0 ; float RAW_SUM = 0.0 ; RSSI = 0.0 ; for(int i=0; i<20; i++) { digitalWrite(RSSI_CS, LOW) ; delay(1) ; RAW_HI = shiftIn(RSSI_DAT, RSSI_CLK, MSBFIRST) ; RAW_LO = shiftIn(RSSI_DAT, RSSI_CLK, MSBFIRST) ; RAW = (RAW_HI << 8) | RAW_LO ; digitalWrite(RSSI_CS, HIGH) ; RAW_SUM += RAW / 200.0 ; delay(19) ; } RSSI = RAW_SUM * 0.2082 - 131.83 ; // Serial.println(RAW_SUM,3) ; } void SerialShow(byte adr, byte value) { Serial.print(adr,DEC); Serial.print("\t0x"); if (value < 0x10) Serial.print("0"); Serial.println(value,HEX); } // ///////////////////////////////////////////////////////////////////// // SUBROUTINES VCO // ///////////////////////////////////////////////////////////////////// // SI564 VARIABLES / CONSTANTS const byte Si564_Address = 0x55 ; void UpdateFreq() { byte Aux ; unsigned long F_LO = 1000 * (F_RX + F_IFT) ; // float is now unsigned long, in kHz // For best performance : unsigned long LSDIV = 1 ; unsigned long HSDIV = 100 ; if(F_LO >= 130000) HSDIV = 84 ; if(debug) { Serial.print("RX : ") ; Serial.println(F_RX,3) ; Serial.print("IF : ") ; Serial.println(F_IFT,3) ; Serial.print("LO : ") ; Serial.println(F_LO,DEC) ; Serial.print("HSDIV : ") ; Serial.println(HSDIV,DEC) ; } unsigned long MULI1 = 28145 ; // = 2^32 / XTAL const double MULI2 = 28145.264062909 ; const long XTAL = 152600 ; // kHz unsigned long TRUE_VCO = F_LO * HSDIV * LSDIV ; // in kHz unsigned long INTEGER = TRUE_VCO / XTAL ; unsigned long REST = TRUE_VCO % XTAL ; unsigned long FRACTION = REST * MULI2 ; // Get Device Ready for Update // Set page register to point to page 0 Si564_Write_Byte( 255, 0x00) ; // Disable FCAL override Si564_Write_Byte( 69, 0x00) ; // Synchronously disable output Si564_Write_Byte( 17, 0x00) ; // ////////////////////////////////////// // ////////////////////////////////////// // HSDIV [7:0] ////////////////////////// Aux = HSDIV & 0x000000FF ; Si564_Write_Byte( 23, Aux) ; // LSDIV[2:0] maps into Reg24[6:4] // LSDIV divide ratio = 1, therefore LSDIV register value = 0 // HSDIV[10:8] maps into Reg24[2:0]. Aux = ((HSDIV & 0x0700) >> 8 ) ; Si564_Write_Byte( 24, Aux) ; // Serial.println("FBDIV") ; // FBDIV [7:0] Aux = (FRACTION & 0x000000FF) ; Si564_Write_Byte( 26, Aux) ; // FBDIV [15:8] Aux = (FRACTION & 0x0000FF00 ) >> 8 ; Si564_Write_Byte( 27, Aux) ; // FBDIV [23:16] Aux = (FRACTION & 0x00FF0000 ) >> 16 ; Si564_Write_Byte( 28, Aux) ; // FBDIV [31:24] Aux = (FRACTION & 0xFF000000 ) >> 24 ; Si564_Write_Byte( 29, Aux) ; // FBDIV [39:32] Aux = INTEGER & 0x00FF ; Si564_Write_Byte( 30, Aux) ; // FBDIV [42:40] Aux = (INTEGER & 0x0700 ) >> 8 ; Si564_Write_Byte( 31, Aux) ; // ////////////////////////////////////// // ////////////////////////////////////// // Start FCAL using new divider values Si564_Write_Byte( 7, 0x08) ; delay(31) ; // Synchronously enable output Si564_Write_Byte( 17, 0x01) ; if(debug) Serial.println("UpdateFreq() complete.") ; } void Si564_Write_Byte( byte Register, byte Value) { byte Error = 0x00 ; Wire.beginTransmission(Si564_Address); Wire.write(Register); Wire.write(Value); Error = Wire.endTransmission(); if(Error > 0x00) Serial.print("ERROR WRITING TO SI564 : 0x0") ; if(Error > 0x00) Serial.println(Error, HEX) ; if(debug) SerialShow(Register, Value) ; } void setup() { Serial.begin(115200) ; Wire.begin() ; // INIT OLED display.begin(SH1106_SWITCHCAPVCC); // SHOW STARTUP SCREEN display.clearDisplay(); display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(4,0); display.println("**** WANMOD ****"); display.drawLine(0, 12, 128, 12, WHITE); display.setTextSize(1); display.setCursor(0,21); display.println("A 30 MHz MEASUREMENT"); display.setCursor(0,33); display.println("RECEIVER FOR CALLISTO"); display.setCursor(0,45); display.println("(C) ETH QUANTUMOPTICS"); display.setCursor(0,57); display.println("BUILT 26.12.2021"); display.display(); delay(9999) ; // KNOB - ROTARY ENCODER pinMode(RE2, INPUT_PULLUP) ; pinMode(RE3, INPUT_PULLUP) ; pinMode(RE4, INPUT_PULLUP) ; attachInterrupt(digitalPinToInterrupt(RE2), ISR2, FALLING) ; attachInterrupt(digitalPinToInterrupt(RE3), ISR3, FALLING) ; attachInterrupt(digitalPinToInterrupt(RE4), ISR4, FALLING) ; // BATTERY VOLTAGE pinMode(SupplyPin, INPUT) ; // RSSI MAX1284 pinMode(RSSI_CLK, OUTPUT) ; pinMode(RSSI_DAT, INPUT_PULLUP) ; pinMode(RSSI_CS, OUTPUT) ; Serial.println("WANMOD 9.9 BY CHANGPUAK.CH") ; Serial.println("(C) 26.12.2021\n") ; UpdateFreq() ; UpDateDisplay() ; Serial.println("READY.\n") ; } void loop() { // ///////////////////////////////////////////// // EVALUATE ROTARY ENCODER : KEY PRESSED // ///////////////////////////////////////////// if(PRESS) { CursorPos += 1 ; if(CursorPos > 4) CursorPos = 0 ; UpDateDisplay() ; PRESS = false ; READY = true ; } // ///////////////////////////////////////////// // EVALUATE ROTARY ENCODER : ROTATE LEFT // ///////////////////////////////////////////// if(LEFT) { switch(CursorPos) { case 0: F_RX -= 10.000 ; break ; case 1: F_RX -= 1.000 ; break ; case 2: F_RX -= 0.100 ; break ; case 3: F_RX -= 0.010 ; break ; case 4: F_RX -= 0.001 ; break ; } if(F_RX < F_RX_MIN) F_RX = F_RX_MIN ; UpdateFreq() ; UpDateDisplay() ; delay(39) ; LEFT = false ; READY = true ; } // ///////////////////////////////////////////// // EVALUATE ROTARY ENCODER : ROTATE RIGHT // ///////////////////////////////////////////// if(RIGHT) { switch(CursorPos) { case 0: F_RX += 10.000 ; break ; case 1: F_RX += 1.000 ; break ; case 2: F_RX += 0.100 ; break ; case 3: F_RX += 0.010 ; break ; case 4: F_RX += 0.001 ; break ; } if(F_RX > F_RX_MAX) F_RX = F_RX_MAX ; UpdateFreq() ; UpDateDisplay() ; delay(39) ; RIGHT = false ; READY = true ; } // ///////////////////////////////////////////// // WAIT FOR USER ACTION // ///////////////////////////////////////////// while(READY) { delay(39); UpdateRSSI() ; UpDateDisplay() ; // ////////////////////////////////// // CHECK SERIAL while (Serial.available() > 0) // ////////////////////////////////// { int tmp; char st[20]; char rx = Serial.read(); // read a single character buffer += rx; //add character to the string buffer if ((rx == '\n') || (rx == '\r')) { buffer.toCharArray(tempbuf, 40); if (buffer.startsWith("SETF:")) { sscanf(tempbuf,"SETF:%s",&st); // extract frequency as floating point number F_RX = strtod(st,NULL); if(F_RX < F_RX_MIN) F_RX = F_RX_MIN ; if(F_RX > F_RX_MAX) F_RX = F_RX_MAX ; UpdateFreq() ; Serial.print("New frequency : ") ; Serial.print(F_RX,4) ; Serial.println(" MHz") ; } else if (buffer.startsWith("POW?")) { UpdateRSSI() ; Serial.print(RSSI,2) ; Serial.println(" dBm") ; } else if (buffer.startsWith("*IDN?")) { Serial.println("WANMOD 9.9 BY CHANGPUAK.CH") ; Serial.println("26.12.2021") ; } buffer = "" ; //erase buffer for next command } } } } // ///////////////////////////////////////////////////////////// // INTERRUPT SERVICE ROUTINES // ///////////////////////////////////////////////////////////// void ISR2() { if(READY) { LEFT = false ; RIGHT = false ; PRESS = false ; byte autre = digitalRead(RE3) ; if (autre > 0) LEFT = true ; if (autre < 1) RIGHT = true ; READY = false ; } } void ISR3() { if(READY) { LEFT = false ; RIGHT = false ; PRESS = false ; byte autre = digitalRead(RE2) ; if (autre > 0) RIGHT = true ; if (autre < 1) LEFT = true ; READY = false ; } } void ISR4() { if(READY) { LEFT = false ; RIGHT = false ; PRESS = true ; READY = false ; } } // ///////////////////////////////////////////////////////////// // END OF FILE. // /////////////////////////////////////////////////////////////