I've found that if I can play with certain timings on power up on radio #2 I can get it to work. Which tells me I still have to figure out something on the i2c bus. In the meantime, I'm going to start dumping code on you guys to abuse or ignore as you see fit.
Now, could this code be better optimized? You bet! But since this is a hobby project and I'm not a programmer (nor do I play one on TV), I just can't be bothered.
First up, well look at the includes and pre-set setup() definitions:
// Arduino sketch to control the uPD858 SSB chassis from Uniden.
// This is the chassis that the following radios are known to be built on:
// Cobra: 138 XLR, 139 XLR, Realistic: TRC-449, TRC-457, TRC-458,
// Robyn: SB-510D, SB-520D, President: Adams, old Grant, old Madison, old Washington
// Teaberry: Stalker 101, Stalker 102, Stalker 202
// Could be adapted to many radios, if you're willing to put in the work.
// Same concept could be used to adapt any 40 channel swtich to control the PLL in a
// 23 channel radio (for those that have PLLs).
// Hardware used to build the prototype.
// 1 x Arduino Pro-mini
// 2 x MCP23017
// 2 x 4.7K ohm resistors
// 22 x 1K ohm resistors
// 1 x Robyn SB-510D
Yes, that's a lot of comments before you actually DO anything. And the reason I specify one Robyn here is I wrote this for the first radio (that works) and never updated it for the second one.
// Pull in library to control MCP23017 chips
#include <Adafruit_MCP23X17.h>
This is the library file that contains the routines that allow the Ardiuno to communicate with the MCP23017 chips.
// Create two instances so that the right data gets sent to the right MCP23017
// mcp1 is connected to the PLL, mcp2 is connected to the channel display
Adafruit_MCP23X17 mcp1;
Adafruit_MCP23X17 mcp2;
Mostly self-explanatory. You'r telling the Arduino there's two MCP23017 chips to talk to. Since Arduino is C++ under the covers, you're basically creating two instances so that the library code can be told which one to address when.
Now we get into arrays:
// Set up an array containing all possible inputs from the channel selector.
byte CHS[41][8]= {{0,1,0,1,0,0,0,0},{0,1,0,1,0,0,0,1},{0,1,0,1,0,0,1,0},{0,1,0,1,0,0,1,1},{0,1,0,1,0,1,0,1},{0,1,0,1,0,1,1,0},{0,1,0,1,0,1,1,1},{0,1,0,1,1,0,0,0},{1,0,0,0,0,0,0,0},{1,0,0,0,0,0,0,1},{1,0,0,0,0,0,1,0},
{1,0,0,0,0,0,1,1},{1,0,0,0,0,1,0,1},{1,0,0,0,0,1,1,0},{1,0,0,0,0,1,1,1},{1,0,0,0,1,0,0,0},{1,0,0,1,0,0,0,0},{1,0,0,1,0,0,0,1},{1,0,0,1,0,0,1,0},{1,0,0,1,0,0,1,1},{1,0,0,1,0,1,0,1},
{1,0,0,1,0,1,1,0},{1,0,0,1,0,1,1,1},{1,0,1,0,0,0,0,0},{1,0,0,1,1,0,0,0},{1,0,0,1,1,0,0,1},{1,0,1,0,0,0,0,1},{1,0,1,0,0,0,1,0},{1,0,1,0,0,0,1,1},{1,0,1,0,0,1,0,0},{1,0,1,0,0,1,0,1},
{1,0,1,0,0,1,1,0},{1,0,1,0,0,1,1,1},{1,0,1,0,1,0,0,0},{1,0,1,0,1,0,0,1},{1,0,1,1,0,0,0,0},{1,0,1,1,0,0,0,1},{1,0,1,1,0,0,1,0},{1,0,1,1,0,0,1,1},{1,0,1,1,0,1,0,0},{1,0,1,1,0,1,0,1}};
This first array is the binary output of the channel switch at each position, plus 1 to make the math work easier. I put the additional one at the front of the array to be in the "zero" slot. This is because in the Arduino environment counting starts at zero by default. You can work around that, but then you have to keep track of it everywhere. So I did it the lazy way.
// The NCODES to program the PLL have some "unique characteristics" on the FCC allotted CB channels.
// This array accounts for the channel "skips" and out of order configuration of channels 23, 24, and 25.
int FCC[41] = {90,91,92,93,95,96,97,98,100,101,102,103,105,106,107,108,110,111,112,113,115,116,117,120,118,119,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135};
Here I call the NCODES that program the PLL as integers rather than binary numbers. This is because the 858 is programmed in 10 digit BCD numbers. Again, I'm lazy so I did this instead of creating a 10 digit BCD array. Plus it makes life easier when going outside the FCC designated channels (for receive only or educational purposes, of course).
// This array has the data for digits 0 through 9. It is also set up for a common anode display, which means the whether a segment is on or not depends on whether it is grounded.
byte BINSVNSEG[10][7] = {{1,0,0,0,0,0,0},{1,1,1,1,0,0,1},{0,1,0,0,1,0,0},{0,1,1,0,0,0,0},{0,0,1,1,0,0,1},{0,0,1,0,0,1,0},{0,0,0,0,0,1,0},{1,1,1,1,0,0,0},{0,0,0,0,0,0,0},{0,0,1,0,0,0,0}}; // Segment order msb to lsb{g,f,e,d,c,b,a}
Here you get to see where I screwed up. I set the segment order backwards from what it should be. So I compensated in software. If you'd rather use some BCD to seven segment decoder latches you can. Which means you could drop this array and use the next one for channel display and PLL programming.
// This array is just 0 through 9 in binary. The sketch converts NCODES to BCD later on, and this comes in handy.
byte BCDDIG[10][4] = {{0,0,0,0},{0,0,0,1},{0,0,1,0},{0,0,1,1},{0,1,0,0},{0,1,0,1},{0,1,1,0},{0,1,1,1},{1,0,0,0},{1,0,0,1}};
It's literally what says in the comment. Allegedly the MCP23017 will automatically convert integers to their digital equivalents, but I didn't want to go to the trouble of laying out each possible BCD digit, converting that what those digits would represent in binary, then building an array for that.
// In case I can be bothered into doing a "band" display option, A, b, and C.
//byte LETSVNSEG[3][7]={{0,0,0,1,0,0,0},{0,0,0,0,0,1,1},{1,0,0,0,1,1,0}};
I haven't around to figuring out a good way to use this yet.
// Set which pins are being used for input that aren't part of the channel selector.
int SW1IN = 10;
int SW2IN = 11;
int SW3IN = 12;
This will be the "band" and +10kc switches
// Grouping all the channel selector pins together
//byte CHSIN[8] = {9,8,7,6,5,4,3,2};
byte CHSIN[8] = {2,3,4,5,6,7,8,9};
You see two possibilities here, because again I screwed up and wired the two radios differently. So I compensated in software.
The rest of this is just setting variables the sketch uses to set everything for FCC channel 1 by default.
// Setting channel 1 as the default. Can't get off channel 1? Channel selector may be disconnected or broken.
byte CHSEL[8] = {0,1,0,1,0,0,0,1};
// Set FCC band as default
int BAND = 2;
// Set all switches off as default.
int SW1 = 0;
int SW2 = 0;
int SW3 = 0;
// Set all other variables to be on channel 1 as default.
int CHNUM = 1;
int CHAN = 1;
int NCODE = 91;
int DISPCHAN = 1;
int SAVENCODE = 91;
int PLLONES = 1;
int PLLTENS = 9;
int PLLHUND = 0;
int ONES = 1;
int TENS = 9;
// Set the +10kc logical switch to off, just in case.
boolean PLUS10 = false;
When the sketch comes up, it will immediately set the radio to FCC channel 1, then read the channel selector and other switches and set the radio for whatever channel those tell it to go to. In practice, you usually don't see the channel 1 unless the radio is actually set there or something's broken.
Next we'll get into the setup() routine.