emonTx Node Communicates to one RFM69Pi, but not another

I have two RFM69Pis on two separate Raspberry Pis. Both Pis are located in physically the same location. Both RFM69Pis are working and getting data from all my nodes, except for my emonTx on node 10, which is showing up on one of the RFM69Pis, but not the other.

Here is the minicom output from the RFM69Pi that sees node 10, with the version and config dump.

[RF12demo.12] O i15 g210 @ 433 MHz q1
OK 22 20 0 0 0 34 13 (-39)
OK 26 123 3 225 1 65 11 221 12 (-71)
OK 23 120 75 85 13 14 0 0 0 (-60)
OK 10 118 253 112 253 136 8 0 0 251 46 0 0 0 0 0 0 0 0 0 0 0 0 5 0 (-38)
OK 1 73 0 73 0 0 0 (-77)
OK 20 243 0 0 0 146 1 24 0 (-43)
OK 23 121 75 75 13 16 0 0 0 (-61)
OK 1 73 0 73 0 0 0 (-72)
OK 26 123 3 227 1 65 11 221 12 (-70)
OK 22 22 0 0 0 106 13 (-39)
OK 1 73 0 73 0 0 0 (-76)
OK 23 122 75 221 12 15 0 0 0 (-60)
OK 10 88 252 140 252 101 10 0 0 233 46 0 0 0 0 0 0 0 0 0 0 0 0 5 0 (-38)

Here is the minicom output from the RFM69Pi that does not see node 10, with the version and configuration dump.

[RF12demo.14] B i2 g210 @ 434 MHz q1
OK 23 122 75 221 12 15 0 0 0 (-64)
OK 1 73 0 73 0 0 0 (-76)
OK 23 123 75 64 13 15 0 0 0 (-61)
OK 19 202 0 0 0 232 1 25 0 (-50)
OK 13 101 6 155 12 (-52)
OK 1 73 0 73 0 0 0 (-74)
OK 22 21 0 0 0 95 13 (-47)
OK 23 124 75 24 13 14 0 0 0 (-60)
OK 1 73 0 73 0 0 0 (-76)
OK 21 241 0 0 0 181 1 25 0 (-54)
OK 26 123 3 233 1 65 11 221 12 (-83)
OK 1 73 0 73 0 0 0 (-75)
OK 23 125 75 34 13 15 0 0 0 (-60)
OK 22 22 0 0 0 95 13 (-48)
OK 1 73 0 73 0 0 0 (-77)
OK 23 126 75 54 13 15 0 0 0 (-59)

The RFM69Pi that sees node 10 is running the factory firmware that came with it when it shipped to me in July of 2015, so I can’t be sure precisely what that code looks like.

The RFM69Pi that doesn’t see node 10 is running the following firmware. Any ideas?

//RFM12Pi V2 with RFM69CW Firmware
//Based on JCW RF12 Demo: https://github.com/jcw/jeelib/tree/master/examples/RF12/RF12demo
//Edited for RFM12Pi and emonPi operation June 2014 by Glyn Hudson and Trystan Lea
//http://OpenEnergyMonitor.org
//https://github.com/openenergymonitor/RFM2Pi

// V1.4 Jan 17
// Compile with platformIO and relase with GH releases using TravisCI

// V1.3 July 15 - add RF trace mode debug and fix node ID isse, merge pb66 pull requests
  // https://github.com/openenergymonitor/RFM2Pi/pull/2
  // https://github.com/openenergymonitor/RFM2Pi/pull/4

// Version V1.0 - Nov 2014
// * 433Mhz default frequency, 15 default node ID

// V0.9 June 2014
// * 210 default network group
// * activity LED to light on startup and each time packet is received

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

/// @dir RF12demo
/// Configure some values in EEPROM for easy config of the RF12 later on.
// 2009-05-06 <[email protected]> http://opensource.org/licenses/mit-license.php

// this version adds flash memory support, 2009-11-19
// Adding frequency features, author JohnO, 2013-09-05
// Major EEPROM format change, refactoring, and cleanup for v12, 2014-02-13

#define RF69_COMPAT 1 // define this to use the RF69 driver i.s.o. RF12

#include <JeeLib.h>
#include <util/crc16.h>
#include <avr/eeprom.h>
#include <avr/pgmspace.h>
#include <util/parity.h>

#define MAJOR_VERSION RF12_EEPROM_VERSION // bump when EEPROM layout changes
#define MINOR_VERSION 2                   // bump on other non-trivial changes
#define VERSION "[RF12demo.14]"           // keep in sync with the above

#define SERIAL_BAUD 38400   // max baud for 8Mhz RFM12Pi http://openenergymonitor.org/emon/node/6244/
#define DATAFLASH   0       // set to 0 for non-JeeLinks, else 4/8/16 (Mbit)
#define LED_PIN     9       // activity LED, comment out to disable


/// Save a few bytes of flash by declaring const if used more than once.
const char INVALID1[] PROGMEM = "\rInvalid\n";
const char INITFAIL[] PROGMEM = "config save failed\n";

#if RF69_COMPAT
  byte trace_mode = 0;
#endif

#define _receivePin 8
static int _bitDelay;
static char _receive_buffer;
static byte _receive_buffer_index;

// TODO: replace with code from the std avr libc library:
//  http://www.nongnu.org/avr-libc/user-manual/group__util__delay__basic.html
void whackDelay (word delay) {
  byte tmp=0;

  asm volatile("sbiw      %0, 0x01 \n\t"
  "ldi %1, 0xFF \n\t"
  "cpi %A0, 0xFF \n\t"
  "cpc %B0, %1 \n\t"
  "brne .-10 \n\t"
  :
  "+r" (delay), "+a" (tmp)
  :
  "0" (delay)
);
}

ISR (PCINT0_vect) {
  char i, d = 0;
  if (digitalRead(_receivePin))       // PA2 = Jeenode DIO2
    return;             // not ready!
  whackDelay(_bitDelay - 8);
  for (i=0; i<8; i++) {
    whackDelay(_bitDelay*2 - 6);    // digitalread takes some time
    if (digitalRead(_receivePin)) // PA2 = Jeenode DIO2
      d |= (1 << i);
  }
  whackDelay(_bitDelay*2);
  if (_receive_buffer_index)
    return;
  _receive_buffer = d;                // save data
  _receive_buffer_index = 1;  // got a byte
}


static byte inChar () {
  byte d;
  if (! _receive_buffer_index)
    return -1;
  d = _receive_buffer; // grab first and only byte
  _receive_buffer_index = 0;
  return d;
}


static unsigned long now () {
  // FIXME 49-day overflow
  return millis() / 1000;
}

static void activityLed (byte on) {
#ifdef LED_PIN
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, on);
#endif
}

static void printOneChar (char c) {
  Serial.print(c);
}

/// @details
/// For the EEPROM layout, see http://jeelabs.net/projects/jeelib/wiki/RF12demo
/// Useful url: http://blog.strobotics.com.au/2009/07/27/rfm12-tutorial-part-3a/

// RF12 configuration area
typedef struct {
  byte nodeId;            // used by rf12_config, offset 0
  byte group;             // used by rf12_config, offset 1
  byte format;            // used by rf12_config, offset 2
byte hex_output   :
  2;   // 0 = dec, 1 = hex, 2 = hex+ascii
byte collect_mode :
  1;   // 0 = ack, 1 = don't send acks
byte quiet_mode   :
  1;   // 0 = show all, 1 = show only valid packets
byte spare_flags  :
  4;
  word frequency_offset;  // used by rf12_config, offset 4
  byte pad[RF12_EEPROM_SIZE-8];
  word crc;
}
RF12Config;

static RF12Config config;
static char cmd;
static word value;
static byte stack[RF12_MAXDATA+4], top, sendLen, dest;
static byte testCounter;

static void showNibble (byte nibble) {
  char c = '0' + (nibble & 0x0F);
  if (c > '9')
    c += 7;
  Serial.print(c);
}

static void showByte (byte value) {
  if (config.hex_output) {
    showNibble(value >> 4);
    showNibble(value);
  }
  else
    Serial.print((word) value);
}

static void showString (PGM_P s) {
  for (;;) {
    char c = pgm_read_byte(s++);
    if (c == 0)
      break;
    if (c == '\n')
      printOneChar('\r');
    printOneChar(c);
  }
}

static word calcCrc (const void* ptr, byte len) {
  word crc = ~0;
  for (byte i = 0; i < len; ++i)
    crc = _crc16_update(crc, ((const byte*) ptr)[i]);
  return crc;
}

static void loadConfig () {
  // eeprom_read_block(&config, RF12_EEPROM_ADDR, sizeof config);
  // this uses 166 bytes less flash than eeprom_read_block(), no idea why
  for (byte i = 0; i < sizeof config; ++ i)
    ((byte*) &config)[i] = eeprom_read_byte(RF12_EEPROM_ADDR + i);
}

static void saveConfig () {
  config.format = MAJOR_VERSION;
  config.crc = calcCrc(&config, sizeof config - 2);
  // eeprom_write_block(&config, RF12_EEPROM_ADDR, sizeof config);
  // this uses 170 bytes less flash than eeprom_write_block(), no idea why
  eeprom_write_byte(RF12_EEPROM_ADDR, ((byte*) &config)[0]);
  for (byte i = 0; i < sizeof config; ++ i)
    eeprom_write_byte(RF12_EEPROM_ADDR + i, ((byte*) &config)[i]);

  if (rf12_configSilent())
    rf12_configDump();
  else
    showString(INITFAIL);
}

static byte bandToFreq (byte band) {
  return band == 4 ? RF12_433MHZ : band == 8 ? RF12_868MHZ : band == 9 ? RF12_915MHZ : 0;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// OOK transmit code

#if RF69_COMPAT // not implemented in RF69 compatibility mode
static void fs20cmd(word house, byte addr, byte cmd) {
}
static void kakuSend(char addr, byte device, byte on) {
}
#else

// Turn transmitter on or off, but also apply asymmetric correction and account
// for 25 us SPI overhead to end up with the proper on-the-air pulse widths.
// With thanks to JGJ Veken for his help in getting these values right.
static void ookPulse(int on, int off) {
  rf12_onOff(1);
  delayMicroseconds(on + 150);
  rf12_onOff(0);
  delayMicroseconds(off - 200);
}


static void fs20sendBits(word data, byte bits) {
  if (bits == 8) {
    ++bits;
    data = (data << 1) | parity_even_bit(data);
  }
  for (word mask = bit(bits-1); mask != 0; mask >>= 1) {
    int width = data & mask ? 600 : 400;
    ookPulse(width, width);
  }
}

static void fs20cmd(word house, byte addr, byte cmd) {
  byte sum = 6 + (house >> 8) + house + addr + cmd;
  for (byte i = 0; i < 3; ++i) {
    fs20sendBits(1, 13);
    fs20sendBits(house >> 8, 8);
    fs20sendBits(house, 8);
    fs20sendBits(addr, 8);
    fs20sendBits(cmd, 8);
    fs20sendBits(sum, 8);
    fs20sendBits(0, 1);
    delay(10);
  }
}

static void kakuSend(char addr, byte device, byte on) {
  int cmd = 0x600 | ((device - 1) << 4) | ((addr - 1) & 0xF);
  if (on)
    cmd |= 0x800;
  for (byte i = 0; i < 4; ++i) {
    for (byte bit = 0; bit < 12; ++bit) {
      ookPulse(375, 1125);
      int on = bitRead(cmd, bit) ? 1125 : 375;
      ookPulse(on, 1500 - on);
    }
    ookPulse(375, 375);
    delay(11); // approximate
  }
}

#endif

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// DataFlash code

#if DATAFLASH
#include "dataflash.h"
#else // DATAFLASH

#define df_present() 0
#define df_initialize()
#define df_dump()
#define df_replay(x,y)
#define df_erase(x)
#define df_wipe()
#define df_append(x,y)

#endif

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

const char helpText1[] PROGMEM =
"\n"
"Available commands:\n"
"  <nn> i     - set node ID (standard node ids are 1..30)\n"
"  <n> b      - set MHz band (4 = 433, 8 = 868, 9 = 915)\n"
"  <nnnn> o   - change frequency offset within the band (default 1600)\n"
"               96..3903 is the range supported by the RFM12B\n"
"  <nnn> g    - set network group (RFM12 only allows 212, 0 = any)\n"
"  <n> c      - set collect mode (advanced, normally 0)\n"
"  t          - broadcast max-size test packet, request ack\n"
"  ...,<nn> a - send data packet to node <nn>, request ack\n"
"  ...,<nn> s - send data packet to node <nn>, no ack\n"
"  <n> q      - set quiet mode (1 = don't report bad packets)\n"
"  <n> x      - set reporting format (0: decimal, 1: hex, 2: hex+ascii)\n"
#if RF69_COMPAT
"  <nnn> y    - enable signal strength trace mode, default:0 (disabled)\n"
"               sample interval <nnn> secs/100 (0.01s-2.5s) eg 10y=0.1s\n"
#endif
"  123 z      - total power down, needs a reset to start up again\n"
"Remote control commands:\n"
"  <hchi>,<hclo>,<addr>,<cmd> f     - FS20 command (868 MHz)\n"
"  <addr>,<dev>,<on> k              - KAKU command (433 MHz)\n"
;

const char helpText2[] PROGMEM =
"Flash storage (JeeLink only):\n"
"    d                                  - dump all log markers\n"
"    <sh>,<sl>,<t3>,<t2>,<t1>,<t0> r    - replay from specified marker\n"
"    123,<bhi>,<blo> e                  - erase 4K block\n"
"    12,34 w                            - wipe entire flash memory\n"
;



static void showHelp () {
  showString(helpText1);
  if (df_present())
    showString(helpText2);
  showString(PSTR("Current configuration:\n"));
  rf12_configDump();
}

static void handleInput (char c) {
  if ('0' <= c && c <= '9') {
    value = 10 * value + c - '0';
    return;
  }

  if (c == ',') {
    if (top < sizeof stack)
      stack[top++] = value; // truncated to 8 bits
    value = 0;
    return;
  }

  if ('a' <= c && c <= 'z') {
    showString(PSTR("> "));
    for (byte i = 0; i < top; ++i) {
      Serial.print((word) stack[i]);
      printOneChar(',');
    }
    Serial.print(value);
    Serial.println(c);
  }

  // keeping this out of the switch reduces code size (smaller branch table)
  if (c == '>') {
    // special case, send to specific band and group, and don't echo cmd
    // input: band,group,node,header,data...
    stack[top++] = value;
    // TODO: frequency offset is taken from global config, is that ok?
    rf12_initialize(stack[2], bandToFreq(stack[0]), stack[1],
    config.frequency_offset);
    rf12_sendNow(stack[3], stack + 4, top - 4);
    rf12_sendWait(2);
    rf12_configSilent();
  }
  else if (c > ' ') {
    switch (c) {

    case 'i': // set node id
      if ((value > 0) && (value < 31)) {
        config.nodeId = (config.nodeId & 0xE0) + (value & 0x1F);
        saveConfig();
        }
      break;

    case 'b': // set band: 4 = 433, 8 = 868, 9 = 915
      value = bandToFreq(value);
      if (value) {
        config.nodeId = (value << 6) + (config.nodeId & 0x3F);
        config.frequency_offset = 1600;
        saveConfig();
      }
      break;

    case 'o':
      { // Increment frequency within band
        // Stay within your country's ISM spectrum management guidelines, i.e.
        // allowable frequencies and their use when selecting operating frequencies.
        if ((value > 95) && (value < 3904)) { // supported by RFM12B
          config.frequency_offset = value;
          saveConfig();
        }
        break;
      }

    case 'g': // set network group
      config.group = value;
      saveConfig();
      break;

    case 'c': // set collect mode (off = 0, on = 1)
      config.collect_mode = value;
      saveConfig();
      break;

    case 't': // broadcast a maximum size test packet, request an ack
      cmd = 'a';
      sendLen = RF12_MAXDATA;
      dest = 0;
      for (byte i = 0; i < RF12_MAXDATA; ++i)
        stack[i] = i + testCounter;
      showString(PSTR("test "));
      showByte(testCounter); // first byte in test buffer
      ++testCounter;
      break;

    case 'a': // send packet to node ID N, request an ack
    case 's': // send packet to node ID N, no ack
      cmd = c;
      sendLen = top;
      dest = value;
      break;

    case 'f': // send FS20 command: <hchi>,<hclo>,<addr>,<cmd>f
      rf12_initialize(0, RF12_868MHZ, 0);
      activityLed(1);
      fs20cmd(256 * stack[0] + stack[1], stack[2], value);
      activityLed(0);
      rf12_configSilent();
      break;

    case 'k': // send KAKU command: <addr>,<dev>,<on>k
      rf12_initialize(0, RF12_433MHZ, 0);
      activityLed(1);
      kakuSend(stack[0], stack[1], value);
      activityLed(0);
      rf12_configSilent();
      break;

    case 'z': // put the ATmega in ultra-low power mode (reset needed)
      if (value == 123) {
        showString(PSTR(" Zzz...\n"));
        Serial.flush();
        rf12_sleep(RF12_SLEEP);
        cli();
        Sleepy::powerDown();
      }
      break;

    case 'q': // turn quiet mode on or off (don't report bad packets)
      config.quiet_mode = value;
      saveConfig();
      break;

    case 'x': // set reporting mode to decimal (0), hex (1), hex+ascii (2)
      config.hex_output = value;
      saveConfig();
      break;

    case 'v': //display the interpreter version and configuration
      displayVersion();
      rf12_configDump();
      break;

    case 'l': // turn activity LED on or off
      activityLed(value);
      break;

    case 'd': // dump all log markers
      if (df_present())
        df_dump();
      break;

    case 'r': // replay from specified seqnum/time marker
      if (df_present()) {
        word seqnum = (stack[0] << 8) | stack[1];
        long asof = (stack[2] << 8) | stack[3];
        asof = (asof << 16) | ((stack[4] << 8) | value);
        df_replay(seqnum, asof);
      }
      break;

    case 'e': // erase specified 4Kb block
      if (df_present() && stack[0] == 123) {
        word block = (stack[1] << 8) | value;
        df_erase(block);
      }
      break;

    case 'w': // wipe entire flash memory
      if (df_present() && stack[0] == 12 && value == 34) {
        df_wipe();
        showString(PSTR("erased\n"));
      }
      break;
#if RF69_COMPAT
    case 'y': // turn signal strength trace mode on or off (rfm69 only)
      trace_mode = value;
      break;
#endif

    default:
      showHelp();
    }
  }

  value = top = 0;
}

static void displayASCII (const byte* data, byte count) {
  for (byte i = 0; i < count; ++i) {
    printOneChar(' ');
    char c = (char) data[i];
    printOneChar(c < ' ' || c > '~' ? '.' : c);
  }
  Serial.println();
}

static void displayVersion () {
  showString(PSTR(VERSION));
}

void setup () {
  activityLed(1);
  delay(100); // shortened for now. Handy with JeeNode Micro V1 where ISP
  // interaction can be upset by RF12B startup process.

  Serial.begin(SERIAL_BAUD);
  Serial.println();
  displayVersion();

  if (rf12_configSilent()) {
    loadConfig();
  }
  else {
    memset(&config, 0, sizeof config);
   config.nodeId = 0x4F;                           // RFM12Pi - 433 MHz, node 15
    config.group = 0xD2;                           // RFM12Pi - default group 210
    config.frequency_offset = 1600;
    config.quiet_mode = true;   // Default flags, quiet on
    saveConfig();
    rf12_configSilent();
  }

  rf12_configDump();
  df_initialize();

  delay(1000);        //rfm12pi keep LED for for 1s to show it's working at startup
  activityLed(0);
}

void loop () {
  if (Serial.available())
    handleInput(Serial.read());

#if RF69_COMPAT
  if (trace_mode == 0) {
#endif
  if (rf12_recvDone()) {
    byte n = rf12_len;
    if (rf12_crc == 0)
    {
      activityLed(1);
      showString(PSTR("OK"));
    }
    else {
      if (config.quiet_mode)
        return;
      showString(PSTR(" ?"));
      if (n > 20) // print at most 20 bytes if crc is wrong
        n = 20;
    }
    if (config.hex_output)
      printOneChar('X');
    if (config.group == 0) {
      showString(PSTR(" G"));
      showByte(rf12_grp);
    }
    printOneChar(' ');
    showByte(rf12_hdr & 0x1F);
    for (byte i = 0; i < n; ++i) {
      if (!config.hex_output)
        printOneChar(' ');
      showByte(rf12_data[i]);
    }
#if RF69_COMPAT
    // display RSSI value after packet data
    showString(PSTR(" ("));
    if (config.hex_output)
      showByte(RF69::rssi);
    else
      Serial.print(-(RF69::rssi>>1));
    showString(PSTR(") "));
#endif
    Serial.println();

    if (config.hex_output > 1) { // also print a line as ascii
      showString(PSTR("ASC "));
      if (config.group == 0) {
        showString(PSTR(" II "));
      }
      printOneChar(rf12_hdr & RF12_HDR_DST ? '>' : '<');
      printOneChar('@' + (rf12_hdr & RF12_HDR_MASK));
      displayASCII((const byte*) rf12_data, n);
    }

    if (rf12_crc == 0) {
      activityLed(1);

      if (df_present())
        df_append((const char*) rf12_data - 2, rf12_len + 2);

      if (RF12_WANTS_ACK && (config.collect_mode) == 0) {
        showString(PSTR(" -> ack\n"));
        rf12_sendStart(RF12_ACK_REPLY, 0, 0);
      }
      activityLed(0);
    }
  }

  if (cmd && rf12_canSend()) {
    activityLed(1);

    showString(PSTR(" -> "));
    Serial.print((word) sendLen);
    showString(PSTR(" b\n"));
    byte header = cmd == 'a' ? RF12_HDR_ACK : 0;
    if (dest)
      header |= RF12_HDR_DST | dest;
    rf12_sendStart(header, stack, sendLen);
    cmd = 0;

    activityLed(0);
  }
  activityLed(0);
#if RF69_COMPAT
  } else {
      rf12_recvDone();
      byte y = (RF69::rssi>>1);
      for (byte i = 0; i < (100-y); ++i) {
          printOneChar('-');
      }
      Serial.print("*");
      for (byte i = 0; i < (y); ++i) {
          printOneChar(' ');
      }
      Serial.print(-y);
      Serial.println("dB");

      delay(trace_mode*10);

  }
#endif
}

Update: I found the v1.2 code (below) and loaded it into the offending RFM69Pi. Node 10 still isn’t coming through, so I am doubting that the firmware version is the difference.

/// @dir RF12demo
/// Configure some values in EEPROM for easy config of the RF12 later on.
// 2009-05-06 <[email protected]> http://opensource.org/licenses/mit-license.php

// this version adds flash memory support, 2009-11-19
// Adding frequency features, author JohnO, 2013-09-05
// Major EEPROM format change, refactoring, and cleanup for v12, 2014-02-13

#define RF69_COMPAT 1 // define this to use the RF69 driver i.s.o. RF12

#include <JeeLib.h>
#include <util/crc16.h>
#include <avr/eeprom.h>
#include <avr/pgmspace.h>
#include <util/parity.h>

#define MAJOR_VERSION RF12_EEPROM_VERSION // bump when EEPROM layout changes
#define MINOR_VERSION 2                   // bump on other non-trivial changes
#define VERSION "[RF12demo.12]"           // keep in sync with the above

#if defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny44__)
#define TINY        1
#define SERIAL_BAUD 38400   // can only be 9600 or 38400
#define DATAFLASH   0       // do not change
#undef  LED_PIN             // do not change
#define rf12_configDump()   // disabled
#else
#define TINY        0
#define SERIAL_BAUD 38400   // adjust as needed
#define DATAFLASH   0       // set to 0 for non-JeeLinks, else 4/8/16 (Mbit)
#define LED_PIN     9       // activity LED, comment out to disable
#endif

/// Save a few bytes of flash by declaring const if used more than once.
const char INVALID1[] PROGMEM = "\rInvalid\n";
const char INITFAIL[] PROGMEM = "config save failed\n";

#if TINY
// Serial support (output only) for Tiny supported by TinyDebugSerial
// http://www.ernstc.dk/arduino/tinycom.html
// 9600, 38400, or 115200
// hardware\jeelabs\avr\cores\tiny\TinyDebugSerial.h Modified to
// moveTinyDebugSerial from PB0 to PA3 to match the Jeenode Micro V3 PCB layout
// Connect Tiny84 PA3 to USB-BUB RXD for serial output from sketch.
// Jeenode AIO2
//
// With thanks for the inspiration by 2006 David A. Mellis and his AFSoftSerial
// code. All right reserved.
// Connect Tiny84 PA2 to USB-BUB TXD for serial input to sketch.
// Jeenode DIO2
// 9600 or 38400 at present.

#if SERIAL_BAUD == 9600
#define BITDELAY 54          // 9k6 @ 8MHz, 19k2 @16MHz
#endif
#if SERIAL_BAUD == 38400
#define BITDELAY 11         // 38k4 @ 8MHz, 76k8 @16MHz
#endif

#define _receivePin 8
static int _bitDelay;
static char _receive_buffer;
static byte _receive_buffer_index;

ISR (PCINT0_vect) {
    char i, d = 0;
    if (digitalRead(_receivePin))       // PA2 = Jeenode DIO2
        return;             // not ready!
    whackDelay(_bitDelay - 8);
    for (i=0; i<8; i++) {
        whackDelay(_bitDelay*2 - 6);    // digitalread takes some time
        if (digitalRead(_receivePin)) // PA2 = Jeenode DIO2
            d |= (1 << i);
    }
    whackDelay(_bitDelay*2);
    if (_receive_buffer_index)
        return;
    _receive_buffer = d;                // save data
    _receive_buffer_index = 1;  // got a byte
}

// TODO: replace with code from the std avr libc library:
//  http://www.nongnu.org/avr-libc/user-manual/group__util__delay__basic.html
void whackDelay (word delay) {
    byte tmp=0;

    asm volatile("sbiw      %0, 0x01 \n\t"
                 "ldi %1, 0xFF \n\t"
                 "cpi %A0, 0xFF \n\t"
                 "cpc %B0, %1 \n\t"
                 "brne .-10 \n\t"
                 : "+r" (delay), "+a" (tmp)
                 : "0" (delay)
                 );
}

static byte inChar () {
    byte d;
    if (! _receive_buffer_index)
        return -1;
    d = _receive_buffer; // grab first and only byte
    _receive_buffer_index = 0;
    return d;
}

#endif

static unsigned long now () {
    // FIXME 49-day overflow
    return millis() / 1000;
}

static void activityLed (byte on) {
#ifdef LED_PIN
    pinMode(LED_PIN, OUTPUT);
    digitalWrite(LED_PIN, !on);
#endif
}

static void printOneChar (char c) {
    Serial.print(c);
}

/// @details
/// For the EEPROM layout, see http://jeelabs.net/projects/jeelib/wiki/RF12demo
/// Useful url: http://blog.strobotics.com.au/2009/07/27/rfm12-tutorial-part-3a/

// RF12 configuration area
typedef struct {
    byte nodeId;            // used by rf12_config, offset 0
    byte group;             // used by rf12_config, offset 1
    byte format;            // used by rf12_config, offset 2
    byte hex_output   :2;   // 0 = dec, 1 = hex, 2 = hex+ascii
    byte collect_mode :1;   // 0 = ack, 1 = don't send acks
    byte quiet_mode   :1;   // 0 = show all, 1 = show only valid packets
    byte spare_flags  :4;
    word frequency_offset;  // used by rf12_config, offset 4
    byte pad[RF12_EEPROM_SIZE-8];
    word crc;
} RF12Config;

static RF12Config config;
static char cmd;
static word value;
static byte stack[RF12_MAXDATA+4], top, sendLen, dest;
static byte testCounter;

static void showNibble (byte nibble) {
    char c = '0' + (nibble & 0x0F);
    if (c > '9')
        c += 7;
    Serial.print(c);
}

static void showByte (byte value) {
    if (config.hex_output) {
        showNibble(value >> 4);
        showNibble(value);
    } else
        Serial.print((word) value);
}

static word calcCrc (const void* ptr, byte len) {
    word crc = ~0;
    for (byte i = 0; i < len; ++i)
        crc = _crc16_update(crc, ((const byte*) ptr)[i]);
    return crc;
}

static void loadConfig () {
    // eeprom_read_block(&config, RF12_EEPROM_ADDR, sizeof config);
    // this uses 166 bytes less flash than eeprom_read_block(), no idea why
    for (byte i = 0; i < sizeof config; ++ i)
        ((byte*) &config)[i] = eeprom_read_byte(RF12_EEPROM_ADDR + i);
}

static void saveConfig () {
    config.format = MAJOR_VERSION;
    config.crc = calcCrc(&config, sizeof config - 2);
    // eeprom_write_block(&config, RF12_EEPROM_ADDR, sizeof config);
    // this uses 170 bytes less flash than eeprom_write_block(), no idea why
    eeprom_write_byte(RF12_EEPROM_ADDR, ((byte*) &config)[0]);
    for (byte i = 0; i < sizeof config; ++ i)
        eeprom_write_byte(RF12_EEPROM_ADDR + i, ((byte*) &config)[i]);

    if (rf12_configSilent())
        rf12_configDump();
    else
        showString(INITFAIL);
}

static byte bandToFreq (byte band) {
     return band == 4 ? RF12_433MHZ : band == 8 ? RF12_868MHZ : band == 9 ? RF12_915MHZ : 0;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// OOK transmit code

#if RF69_COMPAT // not implemented in RF69 compatibility mode
static void fs20cmd(word house, byte addr, byte cmd) {}
static void kakuSend(char addr, byte device, byte on) {}
#else

// Turn transmitter on or off, but also apply asymmetric correction and account
// for 25 us SPI overhead to end up with the proper on-the-air pulse widths.
// With thanks to JGJ Veken for his help in getting these values right.
static void ookPulse(int on, int off) {
    rf12_onOff(1);
    delayMicroseconds(on + 150);
    rf12_onOff(0);
    delayMicroseconds(off - 200);
}

static void fs20sendBits(word data, byte bits) {
    if (bits == 8) {
        ++bits;
        data = (data << 1) | parity_even_bit(data);
    }
    for (word mask = bit(bits-1); mask != 0; mask >>= 1) {
        int width = data & mask ? 600 : 400;
        ookPulse(width, width);
    }
}

static void fs20cmd(word house, byte addr, byte cmd) {
    byte sum = 6 + (house >> 8) + house + addr + cmd;
    for (byte i = 0; i < 3; ++i) {
        fs20sendBits(1, 13);
        fs20sendBits(house >> 8, 8);
        fs20sendBits(house, 8);
        fs20sendBits(addr, 8);
        fs20sendBits(cmd, 8);
        fs20sendBits(sum, 8);
        fs20sendBits(0, 1);
        delay(10);
    }
}

static void kakuSend(char addr, byte device, byte on) {
    int cmd = 0x600 | ((device - 1) << 4) | ((addr - 1) & 0xF);
    if (on)
        cmd |= 0x800;
    for (byte i = 0; i < 4; ++i) {
        for (byte bit = 0; bit < 12; ++bit) {
            ookPulse(375, 1125);
            int on = bitRead(cmd, bit) ? 1125 : 375;
            ookPulse(on, 1500 - on);
        }
        ookPulse(375, 375);
        delay(11); // approximate
    }
}

#endif

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// DataFlash code

#if DATAFLASH
#include "dataflash.h"
#else // DATAFLASH

#define df_present() 0
#define df_initialize()
#define df_dump()
#define df_replay(x,y)
#define df_erase(x)
#define df_wipe()
#define df_append(x,y)

#endif

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

const char helpText1[] PROGMEM =
    "\n"
    "Available commands:\n"
    "  <nn> i     - set node ID (standard node ids are 1..30)\n"
    "  <n> b      - set MHz band (4 = 433, 8 = 868, 9 = 915)\n"
    "  <nnnn> o   - change frequency offset within the band (default 1600)\n"
    "               96..3903 is the range supported by the RFM12B\n"
    "  <nnn> g    - set network group (RFM12 only allows 212, 0 = any)\n"
    "  <n> c      - set collect mode (advanced, normally 0)\n"
    "  t          - broadcast max-size test packet, request ack\n"
    "  ...,<nn> a - send data packet to node <nn>, request ack\n"
    "  ...,<nn> s - send data packet to node <nn>, no ack\n"
    "  <n> q      - set quiet mode (1 = don't report bad packets)\n"
    "  <n> x      - set reporting format (0: decimal, 1: hex, 2: hex+ascii)\n"
    "  123 z      - total power down, needs a reset to start up again\n"
    "Remote control commands:\n"
    "  <hchi>,<hclo>,<addr>,<cmd> f     - FS20 command (868 MHz)\n"
    "  <addr>,<dev>,<on> k              - KAKU command (433 MHz)\n"
;

const char helpText2[] PROGMEM =
    "Flash storage (JeeLink only):\n"
    "    d                                  - dump all log markers\n"
    "    <sh>,<sl>,<t3>,<t2>,<t1>,<t0> r    - replay from specified marker\n"
    "    123,<bhi>,<blo> e                  - erase 4K block\n"
    "    12,34 w                            - wipe entire flash memory\n"
;

static void showString (PGM_P s) {
    for (;;) {
        char c = pgm_read_byte(s++);
        if (c == 0)
            break;
        if (c == '\n')
            printOneChar('\r');
        printOneChar(c);
    }
}

static void showHelp () {
#if TINY
    showString(PSTR("?\n"));
#else
    showString(helpText1);
    if (df_present())
        showString(helpText2);
    showString(PSTR("Current configuration:\n"));
    rf12_configDump();
#endif
}

static void handleInput (char c) {
    if ('0' <= c && c <= '9') {
        value = 10 * value + c - '0';
        return;
    }

    if (c == ',') {
        if (top < sizeof stack)
            stack[top++] = value; // truncated to 8 bits
        value = 0;
        return;
    }

    if ('a' <= c && c <= 'z') {
        showString(PSTR("> "));
        for (byte i = 0; i < top; ++i) {
            Serial.print((word) stack[i]);
            printOneChar(',');
        }
        Serial.print(value);
        Serial.println(c);
    }

    // keeping this out of the switch reduces code size (smaller branch table)
    if (c == '>') {
        // special case, send to specific band and group, and don't echo cmd
        // input: band,group,node,header,data...
        stack[top++] = value;
        // TODO: frequency offset is taken from global config, is that ok?
        rf12_initialize(stack[2], bandToFreq(stack[0]), stack[1],
                            config.frequency_offset);
        rf12_sendNow(stack[3], stack + 4, top - 4);
        rf12_sendWait(2);
        rf12_configSilent();
    } else if (c > ' ') {
        switch (c) {

        case 'i': // set node id
            config.nodeId = (config.nodeId & 0xE0) + (value & 0x1F);
            saveConfig();
            break;

        case 'b': // set band: 4 = 433, 8 = 868, 9 = 915
            value = bandToFreq(value);
            if (value) {
                config.nodeId = (value << 6) + (config.nodeId & 0x3F);
                config.frequency_offset = 1600;
                saveConfig();
            }
            break;

        case 'o': { // Increment frequency within band
// Stay within your country's ISM spectrum management guidelines, i.e.
// allowable frequencies and their use when selecting operating frequencies.
            if ((value > 95) && (value < 3904)) { // supported by RFM12B
                config.frequency_offset = value;
                saveConfig();
            }
#if !TINY
            // this code adds about 400 bytes to flash memory use
            // display the exact frequency associated with this setting
            byte freq = 0, band = config.nodeId >> 6;
            switch (band) {
                case RF12_433MHZ: freq = 43; break;
                case RF12_868MHZ: freq = 86; break;
                case RF12_915MHZ: freq = 90; break;
            }
            uint32_t f1 = freq * 100000L + band * 25L * config.frequency_offset;
            Serial.print((word) (f1 / 10000));
            printOneChar('.');
            word f2 = f1 % 10000;
            // tedious, but this avoids introducing floating point
            printOneChar('0' + f2 / 1000);
            printOneChar('0' + (f2 / 100) % 10);
            printOneChar('0' + (f2 / 10) % 10);
            printOneChar('0' + f2 % 10);
            Serial.println(" MHz");
#endif
            break;
        }

        case 'g': // set network group
            config.group = value;
            saveConfig();
            break;

        case 'c': // set collect mode (off = 0, on = 1)
            config.collect_mode = value;
            saveConfig();
            break;

        case 't': // broadcast a maximum size test packet, request an ack
            cmd = 'a';
            sendLen = RF12_MAXDATA;
            dest = 0;
            for (byte i = 0; i < RF12_MAXDATA; ++i)
                stack[i] = i + testCounter;
            showString(PSTR("test "));
            showByte(testCounter); // first byte in test buffer
            ++testCounter;
            break;

        case 'a': // send packet to node ID N, request an ack
        case 's': // send packet to node ID N, no ack
            cmd = c;
            sendLen = top;
            dest = value;
            break;

        case 'f': // send FS20 command: <hchi>,<hclo>,<addr>,<cmd>f
            rf12_initialize(0, RF12_868MHZ, 0);
            activityLed(1);
            fs20cmd(256 * stack[0] + stack[1], stack[2], value);
            activityLed(0);
            rf12_configSilent();
            break;

        case 'k': // send KAKU command: <addr>,<dev>,<on>k
            rf12_initialize(0, RF12_433MHZ, 0);
            activityLed(1);
            kakuSend(stack[0], stack[1], value);
            activityLed(0);
            rf12_configSilent();
            break;

        case 'z': // put the ATmega in ultra-low power mode (reset needed)
            if (value == 123) {
                showString(PSTR(" Zzz...\n"));
                Serial.flush();
                rf12_sleep(RF12_SLEEP);
                cli();
                Sleepy::powerDown();
            }
            break;

        case 'q': // turn quiet mode on or off (don't report bad packets)
            config.quiet_mode = value;
            saveConfig();
            break;

        case 'x': // set reporting mode to decimal (0), hex (1), hex+ascii (2)
            config.hex_output = value;
            saveConfig();
            break;

        case 'v': //display the interpreter version and configuration
            displayVersion();
            rf12_configDump();
#if TINY
            Serial.println();
#endif
            break;

// the following commands all get optimised away when TINY is set

        case 'l': // turn activity LED on or off
            activityLed(value);
            break;

        case 'd': // dump all log markers
            if (df_present())
                df_dump();
            break;

        case 'r': // replay from specified seqnum/time marker
            if (df_present()) {
                word seqnum = (stack[0] << 8) | stack[1];
                long asof = (stack[2] << 8) | stack[3];
                asof = (asof << 16) | ((stack[4] << 8) | value);
                df_replay(seqnum, asof);
            }
            break;

        case 'e': // erase specified 4Kb block
            if (df_present() && stack[0] == 123) {
                word block = (stack[1] << 8) | value;
                df_erase(block);
            }
            break;

        case 'w': // wipe entire flash memory
            if (df_present() && stack[0] == 12 && value == 34) {
                df_wipe();
                showString(PSTR("erased\n"));
            }
            break;

        default:
            showHelp();
        }
    }

    value = top = 0;
}

static void displayASCII (const byte* data, byte count) {
    for (byte i = 0; i < count; ++i) {
        printOneChar(' ');
        char c = (char) data[i];
        printOneChar(c < ' ' || c > '~' ? '.' : c);
    }
    Serial.println();
}

static void displayVersion () {
    showString(PSTR(VERSION));
#if TINY
    showString(PSTR(" Tiny"));
#endif
}

void setup () {
    delay(100); // shortened for now. Handy with JeeNode Micro V1 where ISP
                // interaction can be upset by RF12B startup process.

#if TINY
    PCMSK0 |= (1<<PCINT2);  // tell pin change mask to listen to PA2
    GIMSK |= (1<<PCIE0);    // enable PCINT interrupt in general interrupt mask
    // FIXME: _bitDelay has not yet been initialised here !?
    whackDelay(_bitDelay*2); // if we were low this establishes the end
    pinMode(_receivePin, INPUT);        // PA2
    digitalWrite(_receivePin, HIGH);    // pullup!
    _bitDelay = BITDELAY;
#endif

    Serial.begin(SERIAL_BAUD);
    Serial.println();
    displayVersion();

    if (rf12_configSilent()) {
        loadConfig();
    } else {
        memset(&config, 0, sizeof config);
        
        //JeeLabs new RFM12 Demo 
        //config.nodeId = 0x81;       // 868 MHz, node 1
        //config.group = 0xD4;        // default group 212
        
        //RFM12Pi default 
        config.nodeId = 0x81; // node A1 @ 868 MHz
        config.group = 0xD2;  //210
        
        config.frequency_offset = 1600;
        config.quiet_mode = true;   // Default flags, quiet on
        saveConfig();
        rf12_configSilent();
    }

    rf12_configDump();
    df_initialize();
#if !TINY
    showHelp();
#endif
}

void loop () {
#if TINY
    if (_receive_buffer_index)
        handleInput(inChar());
#else
    if (Serial.available())
        handleInput(Serial.read());
#endif
    if (rf12_recvDone()) {
        byte n = rf12_len;
        
        //if (rf12_crc == 0)
            //showString(PSTR("OK"));                   //strip out since we don't use this on RFM12Pi 
        //else 
        
        if (rf12_crc != 0)
        {
            if (config.quiet_mode)
                return;
            showString(PSTR(" ?"));
            if (n > 20) // print at most 20 bytes if crc is wrong
                n = 20;
        }
        if (config.hex_output)
            printOneChar('X');
        if (config.group == 0) {
            showString(PSTR(" G"));
            showByte(rf12_grp);
        }
        printOneChar(' ');
        showByte(rf12_hdr);
        for (byte i = 0; i < n; ++i) {
            if (!config.hex_output)
                printOneChar(' ');
            showByte(rf12_data[i]);
        }
#if RF69_COMPAT
        // display RSSI value after packet data
        showString(PSTR(" ("));
        if (config.hex_output)
            showByte(RF69::rssi);
        else
            Serial.print(-(RF69::rssi>>1));
        showString(PSTR(") "));
#endif
        Serial.println();

        if (config.hex_output > 1) { // also print a line as ascii
            showString(PSTR("ASC "));
            if (config.group == 0) {
                showString(PSTR(" II "));
            }
            printOneChar(rf12_hdr & RF12_HDR_DST ? '>' : '<');
            printOneChar('@' + (rf12_hdr & RF12_HDR_MASK));
            displayASCII((const byte*) rf12_data, n);
        }

        if (rf12_crc == 0) {
            activityLed(1);

            if (df_present())
                df_append((const char*) rf12_data - 2, rf12_len + 2);

            if (RF12_WANTS_ACK && (config.collect_mode) == 0) {
                showString(PSTR(" -> ack\n"));
                rf12_sendStart(RF12_ACK_REPLY, 0, 0);
            }
            activityLed(0);
        }
    }

    if (cmd && rf12_canSend()) {
        activityLed(1);

        showString(PSTR(" -> "));
        Serial.print((word) sendLen);
        showString(PSTR(" b\n"));
        byte header = cmd == 'a' ? RF12_HDR_ACK : 0;
        if (dest)
            header |= RF12_HDR_DST | dest;
        rf12_sendStart(header, stack, sendLen);
        cmd = 0;

        activityLed(0);
    }
}

Can you set quiet mode to off on the 2nd (not receiving node 10) RFM69Pi?

That way we might see if there are any discarded packets resembling node 10, the discarded packets will be on lines starting with a question mark.

With very little to go on at this point I might be tempted to have a guess and say the emonTx firmware is the issue (in-directly) , later emonTx FW doesn’t send unused temp sensor values as zero’s, they are 300 and above to indicate no conection or missing sensor etc.

The main benifit of that is it fixes a “bitslip” issue where long arrays of zero bits do not allow the reciever to synchronize and correctly decode the packet, a slight difference in component tolerances (or even slight position differences) might mean that one reciever is less error prone than the other.

Either way the odds are, you will have issues with the emonTx being received reliably at some point if you are not using the temp sensors and do not update the FW. So I would say that is a good place to start unless the “quiet mode off” shows something more compelling.

Just to make sure: in minicom, I can type “0q” to disable quiet mode, and “1q” to enable it. Correct?

Correct :smile:

Thanks. I disabled quiet mode and didn’t get any bad packets. Interesting: I didn’t get ANY bad packets. None. That’s why I checked to make sure I was doing the correct command to disable quiet mode. I would have expected at least some bad packets to be out there.

I can’t update the firmware of the emonTx for a couple days, but will try that when I get a chance. But it seems to me that I’m either not seeing the bad packets out there, or that troublesome packets aren’t the problem.

If you look at the first line of your sample output, the line ends in “q1” to signify quite node active.

When you issue a “0q” is three a settings confirmation without the “q1”? There is no explict confirmation of being “off”, just not saying it’s on implies it’s off.

You can also get the current settings status by typong a “v”.

Just to trying to confirm quiet mode is actually being disabled.

That is helpful. I can now confirm quiet mode is being disabled.

[RF12demo.14] B i2 g210 @ 434 MHz q1

[RF12demo.14] B i2 g210 @ 434 MHz

(By the way, why does it show that char bitmasked with ‘@’ to start that output? The ‘B’ in my case?)

The upshot is that with quiet mode confirmed to be disabled, I’m still not seeing any bad packets.

[RF12demo.14] B i2 g210 @ 434 MHz
OK 23 253 82 64 13 15 0 0 0 (-62)
OK 1 66 0 73 0 0 0 (-76)
OK 22 21 0 0 0 116 13 (-47)
OK 26 65 3 112 2 58 11 221 12 (-59)
OK 1 66 0 73 0 0 0 (-76)
OK 23 254 82 95 13 14 0 0 0 (-62)
OK 19 199 0 0 0 217 1 25 0 (-50)
OK 13 134 6 155 12 (-51)
OK 1 66 0 73 0 0 0 (-76)
OK 23 255 82 85 13 16 0 0 0 (-62)

To be honest, I do not know, it was carried over from the JeeLib RF12demo sketch, it’s a different representation of the nodeid (baseid) your other RFM69Pi is “O” (ie 15th letter of the alphabet, in caps), but why that’s done or needed I haven’t a clue. I have just kept it for consistency/compatability with JeeLib and JeeLabs devices e.g. a JeeLink can work just as well as an RFM69Pi with emonhub because the protocol and data formats are identical.

That does seem a bit strange to me too, but it’s not impossible I guess. If it was me, I would probably install a known good FW just as a sanity check as you are running a very recent FW version and we know this definitely worked in the past. You can always swap back after testing.

By the way, you have posted the FW here in full, is that because you have modified it in any way?

That gave me an idea. I disabled quiet mode on the RFM69Pi that can see node 10. Indeed, there are some bad packets floating around.

 ? 35 177 164 221 83 90 207 146 223 231 14 176 89 135 249 243 111 86 3 46 82 (-
OK 22 22 0 0 0 116 13 (-40)
 ? 151 214 140 148 238 127 144 34 170 55 29 182 103 231 179 137 143 249 130 53
OK 1 72 0 73 0 0 0 (-75)
OK 23 169 109 106 13 15 0 0 0 (-63)
OK 10 79 0 116 1 171 1 0 0 40 47 0 0 0 0 0 0 0 0 0 0 0 0 5 0 (-36)
OK 1 72 0 73 0 0 0 (-81)
OK 21 224 0 0 0 188 1 25 0 (-52)
 ? 178 17 119 169 169 250 196 68 12 25 81 48 73 16 14 113 63 98 122 10 96 (-95)
OK 23 170 109 4 13 14 0 0 0 (-63)
 ? 139 221 4 54 141 199 137 64 144 104 247 6 240 194 169 3 231 47 194 220 187 (
OK 1 72 0 73 0 0 0 (-75)
OK 22 20 0 0 0 64 13 (-39)
OK 10 74 0 154 1 182 1 0 0 44 47 0 0 0 0 0 0 0 0 0 0 0 0 5 0 (-36)
OK 23 171 109 54 13 14 0 0 0 (-64)
 ? 79 96 195 218 221 27 85 68 56 205 151 147 112 235 170 29 68 55 49 164 73 (-9
OK 1 72 0 73 0 0 0 (-76)

That was actually my goal. In fact I was hoping to get to the exact same firmware that is running in the RFM69Pi that can see node 10. The v1.2 code I found and posted above reports the same version number but I doubt it is identical because it seemed to switch the group to 0 when I loaded it. Can you point me to good firmware to use as a sanity check? I started looking here. I ended up looking through commits here.

No modifications. If I can get it seeing node 10 I have plans to make some. The reason for posting the FW in full was my doubt about my ability navigate GitHub and find an older, known-to-work FW.

Another data point.

In the interest of finding a good FW to see if I could get the RFM69Pi to see node 10, I put in the one I found here.

Now I don’t see any traffic whatsoever. Tried toggling the group, the node ID, the frequency back and forth. Nothing.

So I went back to each of the FW versions attached above. Still not seeing any traffic of any kind.

Is it possible that somewhere along the way one of these FW versions set some register that doesn’t get reset during initialization?

Update: I downloaded the firmware that is currently in the RFM69Pi that can see node 10. Then I flashed this hex into the one that couldn’t see node 10. It worked! Now it is seeing node 10. So it must be something to do with the FW. So the question now is: what is the difference?

[RF12demo.12] O i15 g210 @ 433 MHz q1
OK 23 1 118 85 13 15 0 0 0 (-68)
OK 10 24 249 229 248 187 16 0 0 43 47 0 0 0 0 0 0 0 0 0 0 0 0 5 0 (-43)
OK 22 20 0 0 0 64 13 (-49)

How bizarre!

How are you compiling? Are you using the Arduino IDE and latest JeeLib or are you using Platformio?

Node 10 is used a lot, I would have thought this would have come up before now.

Was the “faulty” FW on that 2nd RFM69Pi “as supplied” from the shop or not?

I am using Arduino IDE and JeeLib, though I doubt I have updated JeeLib in a while. I use the “Arduino Pro or Pro mini” board option and select “ATmega328p (3.3v 8MHz)” when compiling. That’s based on this discussion.

The “faulty” FWs are the ones I compiled from source as described above, using the sketches I pasted earlier in this thread.

The “good” FW is the one that came from the shop, and which I “recovered” by copying it out of the “working” RFM69Pi and flashing it back in.

So I think the problem is either with the particular sketches I am compiling (the ones above) or with the way I am compiling them.

Update: Got it working. It was indeed an out-of-date jeelib library.

I happened upon this conversation and this comment:

I didn’t realize this either. So I went and got the oem jeelib library from GitHub and boom. Node 10 is back.

It gets odder! Do you mean this repo?

If so that was intended to be used with the RFu versions of emonTx and emonTH, which are long since retired.

Have you tried just updating your out of date JeeLib to the latest ?

The OEM RFu_jeelib is also “out of date” as it hasn’t bbeen updated since the RFu based devices ceased production.

No. After reading this post I realized I should look for an OEM-specific jeelib library.

I went to GitHub > Open Energy Monitor > jeelib and found this repository, which is the one that worked.