APRS Weather Station

Automatic Packet Reporting System (APRS) is amateur radio -based system for sending and receiving short telemetry data locally. Local APRS stations can be seen at www.aprs.fi.

Picture of my station.

My hardware is based on Raspberry Pi computer and home made antenna with Baofeng UV-5R+Plus handheld transceiver. There is also a diy cable between Raspberry and radio. It is made from handsfree headset. There is a galvanic isolation between audio lines and opto-isolator on the PTT line. PTT is connected to Raspberrys GPIO pin 26.

Add this line to Dire Wolf config file:

PTT GPIO 26

Galvanic isolation and attenuation is made with following circuit:

DS18B20 temperature sensor is connected to Raspberrys GPIO pin 4 with parasitic power supply.

You must also activate parasitic power supply by adding following lines to associated files:

In /etc/modules

w1-gpio pullup=1
w1-therm

In /boot/config.txt

dtoverlay=w1-gpio,gpiopin=4,pullup=on

And then reboot.

On Raspberry Pi there is Xastir and Dire Wolf software installed. They are connected together with networked AGWPE. Temperature data is gathered with self made Python code. It talks with Xastir by emulating WX200 weather station. Code is here.

# WX200emu.py
#
# wx200, wx-200 weather station emulation and server
#
# Author:	Juha-Pekka Varjonen
#		 juvar@mbnet.fi
#
# License:	GNU General Public License, Version 3

version = '1.0'

import argparse, socket, sys, datetime, time

parser = argparse.ArgumentParser(prog='WX200Emu.py', description='WX200emu is a weather station emulator and server for client software. Listens for client connections and sends the 1-wire temperature data out to those clients.')
parser.add_argument("host", 
          help="host name or ip address, e.g. localhost")
parser.add_argument("port", 
          help="port number, e.g. 9753",
                    type=int)
parser.add_argument("id", help="1-wire sensor 64-bit serial number")
group = parser.add_mutually_exclusive_group()
group.add_argument("-v", "--verbose",
          help="increase output verbosity",
                    action="store_true")
group.add_argument("-q", "--quiet",
          help="do not display any message",
                    action="store_true")
parser.add_argument('-V', '--version', 
          action='version', 
          version='%(prog)s ' + version)
args = parser.parse_args()

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Bind the socket to the port
server_address = (args.host, args.port)
if args.verbose:
  print('starting up on %s port %s' % server_address)
try:
  sock.bind(server_address)
except:
  if args.quiet is False:
    print('cannot bind socket')
    print('reason: ', sys.exc_info()[1])
  exit()

# Listen for incoming connections
sock.listen(1)

while True:
  try:
    # Wait for a connection
    if args.verbose:
      print('waiting for a connection')
    elif args.quiet is False:
      print('server is up and running')
    connection, client_address = sock.accept()
  except:
    if args.quiet is False:
      print('exception occurred')
      print('reason: ', sys.exc_info()[0])
    exit()
  try:
    if args.verbose:
      print('connection from', client_address)
    while True:
      # current date and time
      i = datetime.datetime.now()
      second = int(str(i.second),16).to_bytes(1, byteorder='big')
      minute = int(str(i.minute),16).to_bytes(1, byteorder='big')
      hour = int(str(i.hour),16).to_bytes(1, byteorder='big')
      day = int(str(i.day),16).to_bytes(1, byteorder='big')
      format_month = (0b00010000 + i.month).to_bytes(1, byteorder='big')

      # temperature to human readable format from 1-wire
      w1file = open('/sys/bus/w1/devices/' + args.id + '/w1_slave', 'r')
      text = w1file.read()
      w1file.close()
      out_temp = float("%.1f" % float(float(text.split("t=")[1])/1000))
      
      # outdoor temperature to wx200 format
      out_temp_sign = 0
      if out_temp < 0:
        out_temp_sign = 8
      out_temp_a = int((abs(out_temp) / 10) + out_temp_sign).to_bytes(1, byteorder='big')
      t = str(int(abs(out_temp) * 10))
      out_temp_bc = int(t[-2:len(t)],16).to_bytes(1, byteorder='big')

      # weather data wx200 format
      # http://wx200.planetfall.com/wx200.txt
      wx200file = [b''.join([b'\x8f', second, minute, hour, day, format_month, b'\0\0\xee\0\0\0\0\0\0\0\0\0\0\0\xee\0\0\0\0\0\0\0\0\0\0\0\0\0']),
          b''.join([b'\x9f\xee\0\0\0\0\0\0\0\0\0\0\0\0\0\0', out_temp_bc, out_temp_a, b'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0']),
          b'\xaf\xee\xee\xee\xee\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0',
          b'\xbf\0\0\0\0\0\0\0\0\0\0\0\0',
          b'\xcf\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0']

      if args.verbose:
        print('sending data to the client')

      # send data, normal range is 0,2 (first two lines)
      for i in range(1,2):
        Cksum = 0
        for ch in wx200file[i]:
            Cksum += ch
        connection.sendall(b''.join([wx200file[i],(Cksum & 0xff).to_bytes(1, byteorder='big')]))
        
      time.sleep(10)

  except FileNotFoundError:
    if args.quiet is False:
      print('cannot open 1-wire connection')
    exit()
  except:
    if args.quiet is False:
      print('exception occurred')
      print('reason: ', sys.exc_info()[0])
    exit()
  finally:
    # Clean up the connection
    if args.verbose:
      print('closing connection')
    connection.close()

Usage example. Reading command line help:

python3 wx200emu.py -h

Using with temperature sensor installed:

python3 wx200emu.py localhost 8008 10-000801b5a7a6

You can use any free port. Change sensor serial number according with your sensor.

Antenna is made according to these drawings: http://www.users.on.net/~endsodds/jpole.htm

I’m very happy with this antenna. I can receive station over a hundred kilometers.

I bought 3.5 inch touchscreen display and housing for Raspberry Pi, but display turned out to be too small and insensitive for anything useful use. So SSH and VNC connections came to good use. SSH works straight out of the box but VNC needs some fine-tuning.

First install RealVNC both to the host and client computers. Then setting up VNC server to Raspberry Pi is easy as pie with following command:

vncserver-virtual :1

Virtual display number can be any, but it must be the same as next command on client computer:

vncviewer xxx.xxx.xxx.xxx:1

Use Raspberry Pi’s IP address here.

Update – forget Xastir

It is possible to send and receive packets with Dire Wolf alone. First here is custom config file for Dire Wolf:

# OH1FWW config file for Dire Wolf

ACHANNELS 1

CHANNEL 0
MYCALL OH1FWW
MODEM 1200
PTT GPIO 26
PBEACON DELAY=0:30 EVERY=30 VIA=WIDE2-2 LAT=61^25.95N LONG=23^48.85E commentcmd="python ~/read1wire.py"

Use your own call sign, not mine! All commands must be on single line. commentcmd is undocumented feature. It makes it possible to run scripts from Dire Wolf and print results to APRS packet comment line.

Every=30 means that it is repeated every 30 minutes.

Run with custom config file:

direwolf -c custom-config.conf

Dont forget to save this read1wire.py file too:

import time
def readtemp():
    try:
        w1file = open('/sys/bus/w1/devices/28-0000021ebb20/w1_slave', 'r')
        text = w1file.read()
        w1file.close()
        return float("%.1f" % float(float(text.split("t=")[1])/1000))
    except:
        print('cannot read 1-wire sensor. E2')
        exit()
i=0
while True:
    out_temp = readtemp()
    if out_temp == 85.0:
        i = i+1
        time.sleep(2)
    else:
        print('out temp: {} deg.C'.format(out_temp))
        exit()
    if i == 10:
        print('cannot read 1-wire sensor. E1')
        exit()

Change sensor ID to yours.

After that it is again useful to use 3.5 inch built in display because there is not any graphical interface.

Deer Repeller

Convenient way to repel deers from eating trees and bushes in carden (in Finland). This device includes motion detector and twilight switch. When motion detected loud whistles and chirps keeps deers away from trees and bushes.

Here is circuit schematics.

Simple VU-meter

This simple VU-meter allows any kind of audio in and displays its volume on the led bar. Gain is adjustable with potentiometer. Circuit uses LM3915 chip.

Part list

All parts can be found on www.taydaelectronics.com.

DMX Controlled Relay Board

Now automation is made easy. With this nifty module one can control four different application by using an computer with a DMX software installed. Module uses four DMX512 address. Starting address is selectable withing DIP switches. Relay is switched on by sending value over 138 and switched off by sending value under 118. Module remember last states during DMX signal loss.

Easy to solder. Arduino compatible and programmable. Can use BSD licensed free library for receiving DMX data. Example code is coming later.

With this and previously published USB-DMX Interface its very easy to build home automation or stage lightning effects.

All relays capable to drive 5A, 230V loads. Uses only one supply voltage of 5V.

Part list

All parts can be bought from www.taydaelectronics.com.

  • R1, R2, R4, R6, R8 – 10k resistor
  • R3, R5, R7, R9 – 200 ohm resistor
  • C1, C2 – 100nF ceramic disc capacitor
  • C3, C4 – 12pF ceramic disc capacitor
  • D1, D3, D5, D7 – 5mm red led
  • D2, D4, D6, D8 – 1N4004 diode
  • Q1-Q4 – BC547 or 2N3904 general purpose transistor
  • Y1 – 16MHz crystal
  • U1 – atMega328P-PU microcontroller ic
  • U5 – MAX485 line driver ic
  • U2, U3, U4, U6 – HJF-3FF relay
  • SW1 – DIP switch 9 positions
  • J3 – 4 pin header
  • J1 – 2 pin screw terminal
  • J2, J4-J8 – 3 pin screw terminal

USB/Ethernet Relay Card with 8 relays

 

Arduino compatible web server based relay card which also can be used with USB connection. No special soldering skills required. Easy to program with popular ENC28J60 Ethernet controller. All Arduino Ethernet libraries are compatible e.g. this ENC28J60 library. Only one +5V USB power supply needed. All relais can be controlled individually. Relais can drive 5A load from 230V.

Board uses FTDI USB i.c. Arduino bootloader natively supports USB and virtual serial port. Device is programmable via USB port with Arduino IDE or similar programs.

All parts can buy from http://www.taydaelectronics.com. Only exception is ethernet connector. It must include transformers and filter elements.

 

Multipurpose 7-segment Display System (Part 1.)

Here is my latest invention. Arduino compatible multipurpose 7-segment display driver. It can be used in many places. To mention few it can be used in gas station price display, score board display in sport and clock or temperature view on store roof to catch eye.

Controlling format can be any and connection is USB virtual serial port. Power supply needs to supply +12V and +5V. Amperage is in accordance with display count. One display draws max. about 240mA from +12V line when all segments are on.

Main board is populated with all components. Slave modules does not need the atMega328P-PU chip and its related components. Board is connected directly on back of a display element. Display height must be 100mm and connectors distance 106.68-107.00mm.

Data line from last display must connect to the feedback line. Then it is programmatically possible to count displays and it is not necessary to manually import display count to microcontroller. Displays are counted from right to left.

Here is schematics for revision 3.

Bill-Of-Material

All components except the display can buy from www.taydaelectronics.com.

Display, KW1-4003ASA, any similar will go and also with reversed polarity. Common pins 1,8 or 3,8. Select with J8.

Unregulated AC/DC power supply can be used with 12V and 5V voltage regulation kits from Tayda:

One 12V regulator is giving enough power for about four displays. 5V power consumption is much less and not need to worry about it. One 5V regulator is enough for most users.

Cable between modules contains 7 wires:

Last display data line and feedback line connection can be made with mini jumper.

Arduino compatible

On final product Arduino bootloader is preinstalled on to atMega328P-PU chip before installing it to board. First prototype works with Arduino Nano and has one display. Below is program code that supports multiple displays.

 

/* Multipurpose 7-segment Display Driver
 * 
 * Juvar's Electronics Corner
 * http://juvar.mbnet.fi/blog/
 * Juvar
 * juvar@mbnet.fi
 * Juha-Pekka Varjonen
 * 
 * version 1.4
 * 
 * 
 * Master unit is connected to last display in row
 * Leftmost display data output is connected to feedback line
 * 
 * bug in C language:
 * array[0] = cannot write
 * array is zero indexed but cannot save data in to zero position
 */

#define SER 2
#define FB 3
#define OE 4
#define CLK 5

int dispCount;
int pos = 1;

/*    
 *   _A_  
 * F|   |B
 *  |_G_|
 *  |   |
 * E|___|C
 *    D
 */


//DP,e,d,c,b,a,f,g
byte num[12] = {
  0b01111110, /* 0 */
  0b00011000, /* 1 */
  0b01101101, /* 2 */
  0b00111101, /* 3 */
  0b00011011, /* 4 */
  0b00110111, /* 5 */
  0b01110111, /* 6 */
  0b00011100, /* 7 */
  0b01111111, /* 8 */
  0b00111111, /* 9 */
  0b00000000  /* space */
  };
byte dataArr[20];

void setup() {
  // make the pins outputs
  pinMode(SER, OUTPUT); // serial data
  pinMode(OE, OUTPUT); // output enable
  pinMode(CLK, OUTPUT); // clock pulse
  // disable display output
  digitalWrite(OE, HIGH);
  // initialize serial
  Serial.begin(9600);

  // count displays
  int i = 0;
  byte temp;
  //set data
  digitalWrite(SER, HIGH);
  do {
    // toggle clock pulse
    digitalWrite(CLK, HIGH);
    digitalWrite(CLK, LOW);
    i++;
    bitWrite(temp, 0, digitalRead(FB));
    temp = temp << 1;
  } while (temp != 0b11111110);
  dispCount = (i+1)/8;
  // clear data
  digitalWrite(SER, LOW);
}

void loop() {
  // if there's any serial available, read it
  while (Serial.available() > 0) {
    // read the incoming byte
    char data = Serial.read();
    // if any numbers
    if (data >= 48 && data <= 57) dataArr[pos++] = num[data-48];
    // if space
    else if (data == 32) dataArr[pos++] = num[10];
    // if dot
    else if(data == 46) {
      if (pos > 1) bitSet(dataArr[pos-1], 7); 
        else bitSet(dataArr[pos], 7);
    } 
    // if carriage return
    else if (data == 13) updateDisp();
  }
}

void updateDisp() {
  //disable display output
  digitalWrite(OE, HIGH);
  //send data to displays
  int i = 1;
  do {

    for (int e = 0; e <= 7; e++) {
      // send segment data
      digitalWrite(SER, bitRead(dataArr[i],e));
      // toggle clock pulse
      digitalWrite(CLK, HIGH);
      digitalWrite(CLK, LOW);
    }

    dataArr[i] = 0; //fill with space
    i++;    
  } while (i <= dispCount);
  // clear data
  digitalWrite(SER, LOW);
  // toggle clock pulse
  digitalWrite(CLK, HIGH);
  digitalWrite(CLK, LOW);
  // enable display output
  digitalWrite(OE, LOW);
  pos = 1;
}

 

Prototypes accepts serial data from USB virtual serial port with following format. It mimics check point times from some sort of sport. Second prototype has two displays, so thats why data needs to be short. Otherwise data will be truncated.

1.1
2.2
4.5

Example picture with that data.

Data is displayed immediately after receiving it and stays on display until new data is arrived. It is possible to clear display temporarily by setting Output Enable pin to high state or clear display memory with sending empty character to all displays.

Selectors J6, J7 and J8

J6 and J7 selects display type between common anode and common cathode. They are solid jumper wires. If mode needs to change then must also change all transistors and reverse on board zener diode. When J6 is position 2-3 and J7 is position 1-2 then display is common anode. And when J6 is position 1-2 and J7 is position 2-3 then display is common cathode.

With J8 its possible to select whether connected display common pins are 1, 8 or 3, 8. Virtually all display modules are supported. Jumpers in position 1-3 and 2-4 means that 3, 8 is selected. When jumpers are in position 1-2 and 3-4 then 1, 8 is selected.