Airspy HF+

First impression

Pre-ordered it on Nov 11 and got it in the mail today, the 10 dB gain in the MDS (according to the specs on the airspy website) is impressive:

And this is what it looks:

IMG_1697

All specifications can be found on www.airspy.com.

What is the dynamic range and the noise floor?

Since the airspy HF+ comes with an 18 bit ADC rather than the 12 bit ADC for the airspy the dynamic range is extended. The dynamic range in dB is computed as 20 log10( 2^n ) where n is the number of bits of the ADC, this is 108 dB for 18 bits, and it is 72 dB for 12 bits. For the first SDR I once got it was 8 bits, so 48 dB. For the noise floor measurement it means that I have more possibilities to test the SNR than with all earlier software defined receivers.

For the airspy HF+ I measured the SNR of a known signal near 10 MHz which is directly into the receiver. The source is 120 mV rms over a 50 Ohm dummy load, this is attenuated with 42 dB (I need to find something larger actually) and I got an SNR of 69 dB over 500 Hz bandwidth. This corresponds to -146 dB per 500 Hz, add 30 to get dBm, thus -116 dBm over 500 Hz. The noise floor is therefore -143 dBm/Hz, we are 30 dB over the thermal noise floor at room temperature of -173 dbm/Hz, the excess noise is probably around 30dB. The vertical scale in SDRsharp looks like it is in dBm over a 500 Hz bandwidth.

The -116dBm over 500 Hz can be improved by decimating the IQ data, everything was measured at 768 kHz BW for the SDR, and the following steps are 384, 192, 96 and 48 kHz, with each decimation step you yield 3 dB in the noise floor, there are 4 steps so we win 12 dB. In this way I end up at -128 dBm over a 500 Hz window where the SDR decimation is step to 48 kHz.

Work to be done:

  • My results are different compared to what you find on the airspy website, they mention -140 dBm where the bandwidth is not mentioned.
  • The other possibility is that I need to repeat the test with a different signal source, because intermodulation and carrier phase noise of the test source may affect my results.

Last update: 16-Jan-2018

 

Advertisements

Buck converters

Low amperage converters

Are there more efficient ways than the standard voltage regulators such as the LM7805 and the LM317 that we use in most of our experiments? The answer to this rhetorical  question is of course yes, use the buck converter. This video explains how they work:

The following flashlight experiment is an example, in this case a buck converter is used to drive a bright white power led. What you end up with is the following schematic:

schematic
Both 10 Ohm resistors are there to be able to measure the currents I1 and I2.

I’ve put in a blue transparent acrylic case (I’m beginning to like them):

IMG_1582
Design in acrylic case

The DC converter VMA404 is made by Velleman and it is a marvel of simplicity. In this case example it helps to convert the voltage from a 9V 220 mAh battery to a lower voltage (around 3 Volt) for the LED. To compore the required powers I need I1 and I2,  and they are derived from voltages are measured over the resistors. In this way you can compute the efficiency of the converter; that is, if we replaced the buck converter by a resistor, then what power is lost over that resistor compared to the power now lost in the buck converter. I varied the potentiometer on the VMA404 over a considerable range, and this is what I got:

efficiency
Horizontal axis: voltage over the LED, left vertical axis, milliwatt dissipation from battery, or theoretically when the buck converter is replaced by resistors. Right vertical axis, the efficiency of this design in percent relative to the traditional current or voltage limiter.

 

The horizontal axis shows the voltage of the white led and the left vertical axis the power in mW consumed by the flashlight. Blue is what you measure, and red is what you theoretically get when the buck converter is replaced by a traditional voltage or current limiter (LM7805 or LM317). So for this design the buck converter is always more optimal than the traditional design, there is even an optimum near 2,950 volt over the LED, but, this LED can easily handle a higher voltage. Does the VMA404 cause a lot of QRM, the answer is no because the currents are relatively small. You can detect its presence within a meter, but beyond the 1 meter my metrovna with a detection loop didn’t notice it.

High amperage DC/DC converter

Also this is something I’m not going to make myself, the principle is the same except that the conversion goes both ways, DC input voltages within a certain range are converted into one fixed output voltage. For this I tested the Dometic Group DC20 converter, 8 to 16 volts in, and 14.2 volts out up to a maximum 20 Amperes. This is enough for the FT-991 and the FT-857 to produce 100W.

This DC/DC converter is ideal for in the car or in the field when you run on batteries, a LIPO or NiMH etc or a lead battery, as long as it is between 8 and 16 volt. The DC20 can also charge a regular battery, for this please read the manual (I didn’t test it). On the HF I could not directly detect any spurious noise as long as you run it from a battery. There is some leakage of the power switching towards the battery, so if you mount the DC20 behind a regular power supply then the HF switching blurp goes in the power grid, and this will cause spurious signals on the HF, in particular when the buck converter has no load on its output terminal, and the metal case is not grounded. So, to reduce the HF noise with this converter, 1) use a battery, and 2) ground the case of the converter.  I could not detect spurious signals on the HF (or VHF or UHF) when it is used in this way.

The DC20 has a battery low and an overload indicator, is supposedly fail-safe (didn’t test this), and can be put in remote switch-on switch-off mode so that you can install it in a boat, a truck, a car etc. It includes an automatic fan, and there are air inlets that must be kept free. Also, you don’t want to put converters in spaces where there are outgassing batteries, so when you mount it in a closed space the batteries need to be separated from the converter.

20amp buck converter

With the ft-857 (and the ft991) in the field I could oftentimes not make more than 30 to 50W because the voltage of a discharging battery was too low. The radios still work, but the HF power output rapidly goes down. With a DC/DC converter you can bring it back to the 100W we were used to in the shack, without carrying a linear in the field. Some transceivers such as the older icoms are quite critical about the input voltages, in this case a DC/DC converter is a must for mobile operations.

Last update: 31-Dec-2017

Digital voltmeter

IMG_1576
Made with the KT2578 digital panel meter, a 9 volt battery and a 1 to 100 voltage divider. Internal resistance 100k in series with a 1k resistor. Reverse polarity is handled by the KT2578, this gadget is good for checking batteries in the field up to 20 Volt, or measuring amperes over a low ohm resistor.

Last update: 24-dec-2017

Frequency synthesizer project

Model 1 was built to test the capabilities of the AD9851 chip, and to optimize the code. Model 2 is the miniaturized version so that it becomes an instrument in the shack.

Model 1

The design is based on the AD9851 chip including an atmel 328p with the Arduino bootloader. And of course I use the Arduino IDE:

IMG_1530

Top view: AD8951 with a 30 MHz oscillator, I turned the internal 6x multiplier on. There is a jumper for turning the LCD backlight on/off, perhaps I will replace this with a n-channel MOSFET switch.

IMG_1529

The rotary encoder includes a push button, the LCD lists the frequency and the increment setting.

IMG_1551

Attenuation of 30 dB is enough for direct input to the airspy/spyverter which I use as a calibrated spectum analyzer.

The design of this DDS mostly follows from the instructables website. There is a todo list for this project:

  • Find a case, right now it is half the size of a Velleman PCB.
  • Increase the output level to perhaps 5 Volt
  • Investigate whether I can turn this into a VNA

Model 2:

Miniaturize model 1 so that it fits in a Hammond case, the idea is the same, except that I use 3 mini voltage regulators and that I stack the LCD display on top of the atmel 328p chip.  Also I rewired everything because of the space restrictions. In the end I got this:

IMG_1567
The DDS in its mini Hammond box, the window in the lid is a bit critical, but it always turns out to fit. (Use tape and take your time to make the window.
IMG_1568
The only way to do this is to stack the components, below the LCD I’ve put the atmel 328p
IMG_1570
So this is what you get at the lowest level, mind the three mini 5V regulators. The board takes 100 mA from a 9V battery
IMG_1571
And this is the exploded view, not the piece of foam to keep the 9V battery in place.

Last update: 20-dec-2017

Upgrade my DIY antenna tuner

The DIY antenna tuner that I described earlier in this blog showed a few shortcomings, it lacked a counter for the roller coil and the variable capacitor range was often too limited. Yesterday and today I spend time on replacing the coil with one that I got from Frans PA0FMC. I used the same capacitors and added 168 pf addons that apply to C1 and C2, so this adds two switches to the rear side. For the rest the design is still as shown below where by C1 and C2 can now vary between roughly 40 pf and 400pf.

IMG_0465

In this schematic Q is the replacement for the output antenna, and L can vary up to 24 microhenry. This is what you see on the inside, note the new location of the balun in case you want to tune an open line directly, the white switches and 4 Russian high voltage capacitors. The L, C1 and C2 must be isolated from the case, except for the base of the inductor which is connected to the case in the lower left.

diy_tuner_inside

This new frontal look:

diy_tuner_front

The G5RVj in my garden can be tuned on 80m 40m and 20m, for the 20m band I have to switch on the addon-capacitors. This DIY tuner is good for field work and the WSPR beacon, furthermore it is always good to have a second tuner in the shack. and it was fun to make it.

Last update: 2-dec-2017

MAX7219cng LED matrix controller

Playing with the MAX7219cng controlled by the Arduino

It is four years ago I did this, maybe it still works

/* code for max 7219 from maxim,
reduced and optimised for useing more then one 7219 in a row,
______________________________________

 Code History:
 --------------

The orginal code was written for the Wiring board by:
 * Nicholas Zambetti and Dave Mellis /Interaction Design Institute Ivrea /Dec 2004
 * http://www.potemkin.org/uploads/Wiring/MAX7219.txt

First modification by:
 * Marcus Hannerstig/  K3, malm� h�gskola /2006
 * http://www.xlab.se | http://arduino.berlios.de

This version is by:
 * tomek ness /FH-Potsdam / Feb 2007
 * http://design.fh-potsdam.de/ 

 * @acknowledgements: eric f. 

-----------------------------------

General notes: 

-if you are only using one max7219, then use the function maxSingle to control
 the little guy ---maxSingle(register (1-8), collum (0-255))

-if you are using more then one max7219, and they all should work the same,
then use the function maxAll ---maxAll(register (1-8), collum (0-255))

-if you are using more than one max7219 and just want to change something
at one little guy, then use the function maxOne
---maxOne(Max you wane controll (1== the first one), register (1-8),
collum (0-255))

/* During initiation, be sure to send every part to every max7219 and then
 upload it.
For example, if you have five max7219's, you have to send the scanLimit 5 times
before you load it-- other wise not every max7219 will get the data. the
function maxInUse  keeps track of this, just tell it how many max7219 you are
using.
*/
//
// For the keyboard read http://wardyprojects.blogspot.nl/2011/05/74hc165-piso-shift-register-arduino.html
// ---------------------------------------------------------------------------------------------------------------
//
//
// hardware model
//
const int dataIn = 2;      // pin 1 on the 7219
const int load   = 3;      // pin 12 on the 7219
const int clock  = 4;      // pin 13 on the 7219
const int midbutton = 5;   // mid button on matrix board
const int rightbutton = 6; // right button on matrix board
const int leftbutton = 7;  // left button on matrix board
const int maxInUse = 1;    // there is one MAX7219 in use
const int lodpin   = 10;   // purple line keyboard
const int clkpin   = 8;    // white line keyboard
const int serpin   = 9;    // green line keyboard
const int nkeys = 16;      // because there are 16 scanlines on two 74hct165 chips
char keybrd[nkeys];
char keylst[nkeys];
int keycode;
int pkeycode;
int pkeyrec;
const int maxrec = 20;
char keyrec[maxrec];
//
// max7219 registers
//
byte max7219_reg_noop        = 0x00;
byte max7219_reg_digit0      = 0x01;
byte max7219_reg_digit1      = 0x02;
byte max7219_reg_digit2      = 0x03;
byte max7219_reg_digit3      = 0x04;
byte max7219_reg_digit4      = 0x05;
byte max7219_reg_digit5      = 0x06;
byte max7219_reg_digit6      = 0x07;
byte max7219_reg_digit7      = 0x08;
byte max7219_reg_decodeMode  = 0x09;
byte max7219_reg_intensity   = 0x0a;
byte max7219_reg_scanLimit   = 0x0b;
byte max7219_reg_shutdown    = 0x0c;
byte max7219_reg_displayTest = 0x0f;
//
// global variables
//
int dt = 40;  // speed in msec in funny demos routine
int demo;
int looptime = 500;  // wait time between demos
int pbut0 = 0;
int pbut1 = 0;
int pbut2 = 0;
//int automat = 1;    // start in motion
int mindemo = 0;
int maxdemo = 10;
unsigned long int timer = 0;
unsigned long int ptimer = 0;
//unsigned long int action = 0;
unsigned long int timer0 = 0;
int state;  // what is the state of that code if it is read

//------------------------------------------------------------------------------------------

void putByte(byte data) {
  byte i = 8;
  byte mask;
  while(i > 0) {
    mask = 0x01 << (i - 1);      // get bitmask     digitalWrite( clock, LOW);   // tick     if (data & mask){            // choose bit       digitalWrite(dataIn, HIGH);// send 1     }else{       digitalWrite(dataIn, LOW); // send 0     }     digitalWrite(clock, HIGH);   // tock     --i;                         // move to lesser bit   } } //------------------------------------------------------------------------------------------ void maxSingle( byte reg, byte col)  {       //maxSingle is the "easy"  function to use for a     //single max7219   digitalWrite(load, LOW);       // begin        putByte(reg);                  // specify register   putByte(col);//((data & 0x01) * 256) + data >> 1); // put data
  digitalWrite(load, LOW);       // and load da shit
  digitalWrite(load,HIGH);
}

//------------------------------------------------------------------------------------------

void maxAll (byte reg, byte col) {    // initialize  all  MAX7219's in the system
  int c = 0;
  digitalWrite(load, LOW);  // begin
  for ( c =1; c<= maxInUse; c++) {     putByte(reg);  // specify register     putByte(col);//((data & 0x01) * 256) + data >> 1); // put data
  }
  digitalWrite(load, LOW);
  digitalWrite(load,HIGH);
}

//------------------------------------------------------------------------------------------

void maxOne(byte maxNr, byte reg, byte col) {
  //maxOne is for adressing different MAX7219's,
  //whilele having a couple of them cascaded
  int c = 0;
  digitalWrite(load, LOW);  // begin
  for ( c = maxInUse; c > maxNr; c--) {
    putByte(0);    // means no operation
    putByte(0);    // means no operation
  }
  putByte(reg);  // specify register
  putByte(col);//((data & 0x01) * 256) + data >> 1); // put data
  for ( c =maxNr-1; c >= 1; c--) {
    putByte(0);    // means no operation
    putByte(0);    // means no operation
  }
  digitalWrite(load, LOW); // and load the stuff
  digitalWrite(load,HIGH);
}

//------------------------------------------------------------------------------------------

byte flipbyte( byte input )
{
  byte output = 0;
  for (byte i=0; i<8; i++) {
    output = output << 1;     if ((input & B00000001) == B0000001) output = output | B00000001;     input = input >> 1;
  }
  return output;
}

//------------------------------------------------------------------------------------------

void Draw( int irow, int icol ) {
  byte code = 1;
  for (int j=1; j<icol; j++) code = code * 2;
  maxAll(irow,code);
}

//------------------------------------------------------------------------------------------

void UnDraw( int irow ) {
  maxAll(irow,0);
}

//------------------------------------------------------------------------------------------

void funnydemos( int choice )
{
  //
  // demo 0 just counts along rows
  //
  int row;
  int col;
  if (choice == 0) {
    for (row=1; row<=8; row++) { maxAll(row,0); }
    delay(100);
    for (col = 0; col<255; col++) {
      for (row = 1; row<=8; row++) {
        maxAll(row,col+1);
      }
      delay(5);
    }
    delay(100);
    return;
  }
  //
  // demo 1 draws rows
  //
  if (choice == 1) {
    for (row = 1; row<=8; row++) {
      maxAll(row,255);
      delay(dt);
      maxAll(row,0);
    }
    return;
  }
  //
  // demo 2 draws columns
  //
  if (choice == 2) {
    byte code = 1;
    for (col = 1; col<=8; col++) {
      for (row = 1; row<=8; row++) maxAll(row,code);
      delay(dt);
      for (row = 1; row<=8; row++) maxAll(row,0); ;
      code = code * 2;
    }
    return;
  }
  //
  // demo 3 draw a diagonals
  //
  if (choice == 3) {
    for (row=1; row<=8; row++) { maxAll(row,0); }
    delay(10);
    int ok = 0;
    int irow,icol,newrow,newcol,i;
    for (irow = 1; irow<=16; irow++) {
      for (i = -1; i<irow; i++) {         newrow = irow - i;         newcol = i;         if ((newrow >= 1) && (newrow <= 8)) {           if ((newcol >= 1) && (newcol <= 8)) {
            ok = 1;
            row = newrow;
            byte code = 1;
            for (int j=1; j<newcol; j++) code = code * 2;
            maxAll(row,code);
            //Serial.print(newrow); Serial.print(","); Serial.print(newcol); Serial.print(",");  Serial.println(code,HEX);
          }
        }
      }
      //Serial.println(" ");
      delay(dt);
      if (ok == 1) maxAll(row,0);
    }
    return;
  }
  //
  // spiraling
  //
  if (choice == 4) {
    for (row=1; row<=8; row++) { maxAll(row,0); }
    delay(10);
    int irad,irow,icol;
    int ddt = dt/2;
    for (irad = 1; irad<=4; irad++) {
      irow = irad;    for (icol = irad; icol<9-irad; icol++) { Draw(irow,icol); delay(ddt); UnDraw(irow); }
      icol = 9-irad;  for (irow = irad; irow<9-irad; irow++) { Draw(irow,icol); delay(ddt); UnDraw(irow); }       irow = 9-irad;  for (icol = 9-irad; icol>irad; icol--) { Draw(irow,icol); delay(ddt); UnDraw(irow); }
      icol = irad;    for (irow = 9-irad; irow>irad; irow--) { Draw(irow,icol); delay(ddt); UnDraw(irow); }
    }
    delay(300);
    return;
  }
  //
  // something random
  //
  if (choice == 5) {
    for (int repeat=0; repeat < 5; repeat++) {
      for (row = 1; row<=8; row++) {
        byte code = random(0,255);
        maxAll(row,code);
        delay(dt);
      }
    }
  }
  return;
}

//------------------------------------------------------------------------------------------

void DisplayMatrix(char key)
{
  switch ( key ) {
    case '0': // 0
      maxAll( 1,flipbyte(B00111110) );
      maxAll( 2,flipbyte(B00111110) );
      maxAll( 3,flipbyte(B01100011) );
      maxAll( 4,flipbyte(B01100011) );
      maxAll( 5,flipbyte(B01100011) );
      maxAll( 6,flipbyte(B01100011) );
      maxAll( 7,flipbyte(B01111111) );
      maxAll( 8,flipbyte(B00111110) );
      break;
    case '1': // 1
      maxAll( 1,flipbyte(B00001100) );
      maxAll( 2,flipbyte(B00011100) );
      maxAll( 3,flipbyte(B00011100) );
      maxAll( 4,flipbyte(B00001100) );
      maxAll( 5,flipbyte(B00001100) );
      maxAll( 6,flipbyte(B00001100) );
      maxAll( 7,flipbyte(B00001100) );
      maxAll( 8,flipbyte(B00011110) );
      break;
    case '2': // 2
      maxAll( 1,flipbyte(B00111100) );
      maxAll( 2,flipbyte(B01111110) );
      maxAll( 3,flipbyte(B01100110) );
      maxAll( 4,flipbyte(B00001110) );
      maxAll( 5,flipbyte(B00011100) );
      maxAll( 6,flipbyte(B00111000) );
      maxAll( 7,flipbyte(B01111110) );
      maxAll( 8,flipbyte(B01111110) );
      break;
    case '3': // 3
      maxAll( 1,flipbyte(B00111100) );
      maxAll( 2,flipbyte(B01111110) );
      maxAll( 3,flipbyte(B01001110) );
      maxAll( 4,flipbyte(B00011100) );
      maxAll( 5,flipbyte(B00011100) );
      maxAll( 6,flipbyte(B00001110) );
      maxAll( 7,flipbyte(B01111110) );
      maxAll( 8,flipbyte(B00111100) );
      break;
    case '4': // 4
      maxAll( 1,flipbyte(B00000110) );
      maxAll( 2,flipbyte(B00001110) );
      maxAll( 3,flipbyte(B00011110) );
      maxAll( 4,flipbyte(B00110110) );
      maxAll( 5,flipbyte(B01100110) );
      maxAll( 6,flipbyte(B01111110) );
      maxAll( 7,flipbyte(B00000110) );
      maxAll( 8,flipbyte(B00001110) );
      break;
    case '5': // 5
      maxAll( 1,flipbyte(B01111110) );
      maxAll( 2,flipbyte(B01111110) );
      maxAll( 3,flipbyte(B01100000) );
      maxAll( 4,flipbyte(B01111100) );
      maxAll( 5,flipbyte(B01111110) );
      maxAll( 6,flipbyte(B00000110) );
      maxAll( 7,flipbyte(B01111110) );
      maxAll( 8,flipbyte(B01111100) );
      break;
    case '6': // 6
      maxAll( 1,flipbyte(B00111100) );
      maxAll( 2,flipbyte(B01111110) );
      maxAll( 3,flipbyte(B01100000) );
      maxAll( 4,flipbyte(B01100000) );
      maxAll( 5,flipbyte(B01111100) );
      maxAll( 6,flipbyte(B01100110) );
      maxAll( 7,flipbyte(B01111110) );
      maxAll( 8,flipbyte(B00111100) );
      break;
    case '7': // 7
      maxAll( 1,flipbyte(B01111110) );
      maxAll( 2,flipbyte(B01111110) );
      maxAll( 3,flipbyte(B00000110) );
      maxAll( 4,flipbyte(B00001100) );
      maxAll( 5,flipbyte(B00011000) );
      maxAll( 6,flipbyte(B00110000) );
      maxAll( 7,flipbyte(B00110000) );
      maxAll( 8,flipbyte(B00110000) );
      break;
    case '8': // 8
      maxAll( 1,flipbyte(B00111100) );
      maxAll( 2,flipbyte(B01111110) );
      maxAll( 3,flipbyte(B01100110) );
      maxAll( 4,flipbyte(B01111110) );
      maxAll( 5,flipbyte(B00111100) );
      maxAll( 6,flipbyte(B01100110) );
      maxAll( 7,flipbyte(B01111110) );
      maxAll( 8,flipbyte(B00111100) );
      break;
    case '9': // 9
      maxAll( 1,flipbyte(B00111100) );
      maxAll( 2,flipbyte(B01111110) );
      maxAll( 3,flipbyte(B01100110) );
      maxAll( 4,flipbyte(B01111110) );
      maxAll( 5,flipbyte(B00111110) );
      maxAll( 6,flipbyte(B00000110) );
      maxAll( 7,flipbyte(B00111110) );
      maxAll( 8,flipbyte(B00111100) );
      break;
    case '*': // X
      maxAll( 1,B00000000 );
      maxAll( 2,B11000011 );
      maxAll( 3,B01100110 );
      maxAll( 4,B00111100 );
      maxAll( 5,B00011000 );
      maxAll( 6,B00111100 );
      maxAll( 7,B01100110 );
      maxAll( 8,B11000011 );
      break;
    case '#': // X
      maxAll( 1,flipbyte(B00000000) );
      maxAll( 2,flipbyte(B10101010) );
      maxAll( 3,flipbyte(B10101010) );
      maxAll( 4,flipbyte(B11111110) );
      maxAll( 5,flipbyte(B10101010) );
      maxAll( 6,flipbyte(B11111110) );
      maxAll( 7,flipbyte(B10101010) );
      maxAll( 8,flipbyte(B10101010) );
      break;
  }
}

//------------------------------------------------------------------------------------------

void DisplayVars( int v1, int v2 )
{
  //Serial.print(v1); Serial.print(" "); Serial.println(v2);
  int w2 = map(v2,0,1023,0,8);
  switch (w2) {
    case 0:
      maxAll( 1,flipbyte(B00000000) );
      maxAll( 2,flipbyte(B00000000) );
      maxAll( 3,flipbyte(B00000000) );
      maxAll( 4,flipbyte(B00000000) );
      maxAll( 5,flipbyte(B00000000) );
      maxAll( 6,flipbyte(B00000000) );
      maxAll( 7,flipbyte(B00000000) );
      maxAll( 8,flipbyte(B00000000) );
      break;
    case 1:
      maxAll( 1,flipbyte(B00000000) );
      maxAll( 2,flipbyte(B00000000) );
      maxAll( 3,flipbyte(B00000000) );
      maxAll( 4,flipbyte(B00000000) );
      maxAll( 5,flipbyte(B00000000) );
      maxAll( 6,flipbyte(B00000000) );
      maxAll( 7,flipbyte(B00000000) );
      maxAll( 8,flipbyte(B11111111) );
      break;
    case 2:
      maxAll( 1,flipbyte(B00000000) );
      maxAll( 2,flipbyte(B00000000) );
      maxAll( 3,flipbyte(B00000000) );
      maxAll( 4,flipbyte(B00000000) );
      maxAll( 5,flipbyte(B00000000) );
      maxAll( 6,flipbyte(B00000000) );
      maxAll( 7,flipbyte(B11111111) );
      maxAll( 8,flipbyte(B11111111) );
      break;
    case 3:
      maxAll( 1,flipbyte(B00000000) );
      maxAll( 2,flipbyte(B00000000) );
      maxAll( 3,flipbyte(B00000000) );
      maxAll( 4,flipbyte(B00000000) );
      maxAll( 5,flipbyte(B00000000) );
      maxAll( 6,flipbyte(B11111111) );
      maxAll( 7,flipbyte(B11111111) );
      maxAll( 8,flipbyte(B11111111) );
      break;
    case 4:
      maxAll( 1,flipbyte(B00000000) );
      maxAll( 2,flipbyte(B00000000) );
      maxAll( 3,flipbyte(B00000000) );
      maxAll( 4,flipbyte(B00000000) );
      maxAll( 5,flipbyte(B11111111) );
      maxAll( 6,flipbyte(B11111111) );
      maxAll( 7,flipbyte(B11111111) );
      maxAll( 8,flipbyte(B11111111) );
      break;
    case 5:
      maxAll( 1,flipbyte(B00000000) );
      maxAll( 2,flipbyte(B00000000) );
      maxAll( 3,flipbyte(B00000000) );
      maxAll( 4,flipbyte(B11111111) );
      maxAll( 5,flipbyte(B11111111) );
      maxAll( 6,flipbyte(B11111111) );
      maxAll( 7,flipbyte(B11111111) );
      maxAll( 8,flipbyte(B11111111) );
      break;
    case 6:
      maxAll( 1,flipbyte(B00000000) );
      maxAll( 2,flipbyte(B00000000) );
      maxAll( 3,flipbyte(B11111111) );
      maxAll( 4,flipbyte(B11111111) );
      maxAll( 5,flipbyte(B11111111) );
      maxAll( 6,flipbyte(B11111111) );
      maxAll( 7,flipbyte(B11111111) );
      maxAll( 8,flipbyte(B11111111) );
      break;
    case 7:
      maxAll( 1,flipbyte(B00000000) );
      maxAll( 2,flipbyte(B11111111) );
      maxAll( 3,flipbyte(B11111111) );
      maxAll( 4,flipbyte(B11111111) );
      maxAll( 5,flipbyte(B11111111) );
      maxAll( 6,flipbyte(B11111111) );
      maxAll( 7,flipbyte(B11111111) );
      maxAll( 8,flipbyte(B11111111) );
      break;
    case 8:
      maxAll( 1,flipbyte(B11111111) );
      maxAll( 2,flipbyte(B11111111) );
      maxAll( 3,flipbyte(B11111111) );
      maxAll( 4,flipbyte(B11111111) );
      maxAll( 5,flipbyte(B11111111) );
      maxAll( 6,flipbyte(B11111111) );
      maxAll( 7,flipbyte(B11111111) );
      maxAll( 8,flipbyte(B11111111) );
      break;
  }
}

//------------------------------------------------------------------------------------------

void setup()
{
  //
  // signal the world that we are starting
  //
  digitalWrite(13, HIGH);
  delay(1000);
  digitalWrite(13, LOW);
  //
  // Start the serial interface
  //
  Serial.begin(115200);
  Serial.println("Keypad scanner");
  //
  // assign the pins for the max7219
  //
  pinMode(dataIn, OUTPUT);
  pinMode(clock,  OUTPUT);
  pinMode(load,   OUTPUT);
  //
  // attach the keyboard via the 74hc165 piso network
  //
  pinMode(lodpin, OUTPUT);
  pinMode(clkpin, OUTPUT);
  pinMode(serpin, INPUT);
  digitalWrite(lodpin, HIGH);
  digitalWrite(clkpin, LOW);
  //
  //initiation of the max 7219
  //
  maxAll(max7219_reg_scanLimit, 0x07);
  maxAll(max7219_reg_decodeMode, 0x00);  // using an led matrix (not digits)
  maxAll(max7219_reg_shutdown, 0x01);    // not in shutdown mode
  maxAll(max7219_reg_displayTest, 0x00); // no display test
  for (int e=1; e<=8; e++) maxAll(e,0);  // empty registers, turn all LEDs off
  maxAll(max7219_reg_intensity, 0x0f & 0x0f);    // the first 0x0f is the value you can set   // range: 0x00 to 0x0f
  for (int row=1; row<=8; row++) maxAll(row,255);
  //
  // wait a second
  //
  delay(1000);
  funnydemos(0);
  demo = 6;
  timer = millis();
  //action = 0;
  timer0 = timer;
  //automat = 1;
  pkeyrec = -1;
  //
  state = 0;
}

//------------------------------------------------------------------------------------------

void loop()
{
  timer = millis();
  //
  // scan the keyboard
  //
  digitalWrite(lodpin, LOW);
  digitalWrite(lodpin, HIGH);
  int keysum = 0;
  for ( int j=0 ; j<nkeys ; j++) {
    keybrd[nkeys - j - 1] = -1;
    int value = digitalRead(serpin);
    digitalWrite(clkpin, HIGH);
    digitalWrite(clkpin, LOW);
    keybrd[nkeys - j - 1] = value;
    keysum = keysum + value;
  }
  int keymant = 1;
  keycode = 0;
  for (int j=0; j<nkeys; j++) {
    if (keybrd[j] == 1) keycode += keymant;
    keylst[j] = keybrd[j];
    keymant = keymant << 1;   }   //Serial.println(keycode);   char key;   switch (keycode) {     case    0: key = ' '; break;     case    1: key = '*'; break;     case    2: key = '7'; break;     case    4: key = '4'; break;       case    8: key = '1'; break;     case   16: key = '0'; break;     case   32: key = '8'; break;     case   64: key = '5'; break;     case  128: key = '2'; break;     case  256: key = '#'; break;     case  512: key = '9'; break;     case 1024: key = '6'; break;      case 2048: key = '3'; break;          default:   key = '?'; break;   }   if ((keycode != pkeycode) && ((timer - ptimer) > 100)) {
    if ((key != '?') && (key != ' ')) {
      //Serial.print(key);                 // <-- if you want to debug,
      DisplayMatrix(key);
      if (pkeyrec < (maxrec-2)) {
        keyrec[++pkeyrec] = key;
      }
      if (pkeyrec == (maxrec-2)) {
        for (int k=0; k<(maxrec-3); k++) keyrec[k] = keyrec[k+1];
        keyrec[maxrec-2] = key;
      }
    }
    if (key == '#') {
      //Serial.println();                 // <-- if you want to debug
      for (int k=pkeyrec+1; k<maxrec; k++) keyrec[k] = 0;
      //Serial.println(keyrec);           // <-- if you want to debug
      pkeyrec = -1;
      //
      // check the key strings here
      //
      int fail = 1;   // whether there is a proper code
      state = -1;
      if ( strcmp(keyrec,"0#")==0 ) { state = 0; fail = 0; }   // funnydemos 0
      if ( strcmp(keyrec,"1#")==0 ) { state = 1; fail = 0; }   // funnydemos 1
      if ( strcmp(keyrec,"2#")==0 ) { state = 2; fail = 0; }   // funnydemos 2
      if ( strcmp(keyrec,"3#")==0 ) { state = 3; fail = 0; }   // funnydemos 3
      if ( strcmp(keyrec,"4#")==0 ) { state = 4; fail = 0; }   // funnydemos 4
      if ( strcmp(keyrec,"5#")==0 ) { state = 5; fail = 0; }   // funnydemos 5
      if ( strcmp(keyrec,"6#")==0 ) { state = 6; fail = 0; }   // show temperature and light
      Serial.print(keyrec); Serial.print(" -- "); Serial.println(state);
      if (fail == 0) {
        for (int kk=0; kk<strlen(keyrec); kk++) { DisplayMatrix(keyrec[kk]); delay(400); }
        if (state < 6) {
          funnydemos( state ); delay(1000);
        }
      }
    }
    pkeycode = keycode;
  }
  if (keycode != 0) ptimer = timer;
  //
  // repetitive code
  //
  if (timer < timer0) timer0 = timer;   if (timer > timer0) {
    unsigned long int delta = timer - timer0;
    if ((delta % 100) == 0) {
      // every second we arrive here
      if (state == 6) {
        int v1 = analogRead(A4); // temperature
        int v2 = analogRead(A5); // light
        DisplayVars(v1,v2);
        timer0 = timer;
      }
    }
  }
}

Last update: 27-nov-2017