/** HamHead - Remote Head for Ham Radio Copyright (C)2020 Jay Moore/NQ4T - nq4tango@gmail.com Licensed under GPL-3.0-or-later Version .001 Alpha **/ #include #include // Rotary.h uses hardware SPI on Mega2560 pins 52 and 50 or something const uint8_t LEDMATRIX_CS_PIN = 49; // You're not going to have GPIO49 on an Uno. const int NO_OF_DRIVERS = 1; // Each MAX7219 driver can drive eight 7-segment displays. LEDMatrixDriver lmd(NO_OF_DRIVERS, LEDMATRIX_CS_PIN); const byte numBytes = 32; byte rxbytes[numBytes]; // store CI-V incoming bytes here long dec[5]; // part of the decimal to vfo conversion int bc = 0; // byte counter boolean newdata = false; boolean newvfo = false; unsigned long vfoa; byte digit; // used by the display routine. Rotary rotary = Rotary(2, 3); void setup() { Serial1.begin(19200); // I'm using a Mega2560 so I have multiple hardware UART // hardware interrupt for rotary encoder attachInterrupt(0, rotate, CHANGE); attachInterrupt(1, rotate, CHANGE); // copied from display library initialization lmd.setEnabled(true); lmd.setIntensity(2); // 0 = min, 15 = max lmd.setScanLimit(7); // 0-7: Show 1-8 digits. Beware of currenct restrictions for 1-3 digits! See datasheet. lmd.setDecode(0xFF); // Enable "BCD Type B" decoding for all digits. } void loop() { getdata(); // check for serial data if (rxbytes[0] == 0x88) newdata=false; // ignore echo (for now. I have an idea) if (newdata == true) gogovfo(); // Go go VFO! if (newvfo == true) disp(); // update the LED } void getdata() { byte rb; while (Serial1.available() > 0 && newdata == false) { rb = Serial1.read(); if (rb == 0xFE) { // if it's a start byte we don't need it. newdata = false; // make sure we keep looping bc = 0; // i don't trust myself } else if (rb == 0xFD) { // end of the frame rxbytes[bc] = '\0'; // terminate the string newdata = true; // indicate there's new data //i--; // I thought I needed this } else { rxbytes[bc] = rb; // write the byte to the array bc++; // increment byte counter } } } /** "Go Go VFO" is requires some room to explain just what it does. The data sent in is both BCD and little-endian; so I not only have to convert the data from BCD, but I have to load the bytes "backwards". Grab a byte, bitshift it 4 to the left, multiply it by 10. Take that same byte, bitwise AND it aginst 00001111, add them together. Now add those together in to a new array; multiply each entry in the array by the appropriate power of 10, add all that together in to a new variable. Oh, and then set newvfo to true so we can update displays. **/ void gogovfo() { int i = 0; long bcd[4]; bc -= 2; // adjust for array index starting at 0 and ditch the V/U byte if (rxbytes[2] == 0){ for (int x = bc; x > 2; x--) { bcd[i] = (((rxbytes[x] >> 4) *10) + (rxbytes[x]&0xF)); i++; } vfoa = ((bcd[0]*1000000)+(bcd[1]*10000)+(bcd[2]*100)+(bcd[3])); newvfo = true; }} void vfoup() { // this is called by the ISR for the rotary encoder. vfoa += 1000; // right now we're just adding 1000hz, but this will be selectable vfotobcd(); // black magic voodoo newvfo = true; // since we technically updated the VFO } void vfodown() { // this is the same thing, but the other direction vfoa -= 1000; // drop 1000hz vfotobcd(); // do that voodoo that you do so well newvfo = true; // since, you know...new VFO } /** Here's some black magic for ya. In order to go from the internal VFO number, I have to basically reverse the process for getting it form the radio; convert everything in to BCD digits in little-endian. Do a bunch of math on vfoa that has the basic effect of splitting it in to two digit chunks, writing each to an array. Now divide each array entry by 10, bitshift right, add that by the remainder and slap them in a byte. Do that 4 times running backwards through the array. **/ void vfotobcd() { byte bcd2[10]; int dec[5]; int i = 3; dec[0] = vfoa / 1000000; dec[1] = (vfoa % 1000000) / 10000; dec[2] = (vfoa % 10000) / 100; dec[3] = vfoa % 100; for (int x = 0; x < 4; x++){ bcd2[i] = (((dec[x] / 10) << 4) + (dec[x] % 10)); i--; } // There's got to be a better/cleaner way of doing this I haven't figured out yet Serial1.write(0xFE); // start byte Serial1.write(0xFE); // start byte Serial1.write(0x88); // IC-7100 hex address Serial1.write(0xE0); // standard E0 controller address Serial1.write(0x00); // no-reply VFO change Serial1.write(bcd2, 4); // the BCD data Serial1.write(0xFD); // end byte } void disp() { unsigned long vfod = vfoa; // we have to copy this or we'll break vfoa's entry //bc++; // I thought I needed this. /** I know it's a for loop and it looks like it's just getting the remainder and dividing by 10 each time to split out the digits, but I haven't actually figured out how this works. Taken from the library example. **/ for (digit = 0; digit < 8; digit++) { lmd.setDigit(digit, vfod % 10, digit == 3); vfod /= 10; } lmd.display(); delay(10); // set these false or you're going to have problems newdata = false; newvfo = false; } // this came right from the rotary library example. void rotate() { unsigned char result = rotary.process(); if (result == DIR_CW) { vfoup(); } else if (result == DIR_CCW) { vfodown(); } }