Web based ATV Relay Control: Unterschied zwischen den Versionen

Aus DL8RDS Wiki
Wechseln zu: Navigation, Suche
(Raspberry Pi Code)
Zeile 459: Zeile 459:
  
 
== Raspberry Pi Code ==
 
== Raspberry Pi Code ==
 +
 +
This code gives me the same behaviour as a minicom session. It also allows to chain invocations on the commandline:
 +
 +
<pre>
 +
#!/usr/bin/env python
 +
 +
# (c) 2018 Markus Heller, M.A. DL8RDS, relix GmbH
 +
 +
import time
 +
import serial
 +
import sys
 +
import cgi
 +
import json
 +
import os
 +
from getpass import getpass
 +
#from time import sleep
 +
 +
# configure the serial connections (the parameters differs on the device you are connecting to)
 +
ser = serial.Serial(
 +
    port='/dev/ttyACM0',
 +
    baudrate=9600,
 +
    bytesize=serial.EIGHTBITS,
 +
    parity=serial.PARITY_NONE,
 +
    stopbits=serial.STOPBITS_ONE
 +
)
 +
 +
ser.isOpen()
 +
 +
def interaction(input):
 +
    ser.write(input + '\r')
 +
    ser.flush()
 +
    time.sleep(0.8)
 +
    out = ''
 +
    while ser.inWaiting() > 0:
 +
        out += ser.read(1)
 +
 +
    return out[len(input)+2:]
 +
 +
def singlecommand(input):
 +
    print interaction(input)
 +
 +
def cgicommand():
 +
    print("Content-Type: text/html\n\n")
 +
    arguments = cgi.FieldStorage()
 +
    command = arguments.getvalue('command')
 +
    if command:
 +
        interaction(command)
 +
 +
def loop():
 +
    print 'Enter your commands below.'
 +
    print 'Insert "exit" to leave the application.'
 +
   
 +
    while 1 :
 +
        out=interaction("")
 +
        if out.endswith(">> "):
 +
            print "Comms Init successful"
 +
            break
 +
    print "CMD",
 +
 +
    while 1 :
 +
        # get keyboard input
 +
        input = raw_input(">> ")
 +
 +
        if input in ['exit', 'e', 'q']:
 +
            ser.close()
 +
            exit()
 +
        else:
 +
            out = interaction(input)[:-4]
 +
            print out,
 +
 +
 +
if __name__ == "__main__":
 +
    if len(sys.argv) == 1:
 +
        loop()
 +
    else:
 +
        for arg in sys.argv[1:]:
 +
            singlecommand(arg)
 +
</pre>
  
 
== Pictures ==
 
== Pictures ==
  
 
TBD
 
TBD

Version vom 7. Januar 2018, 02:11 Uhr

1 Scope

Out ATV relay DB0MHB is a microcontroller based system that can be controlled through DTMF tones, but is still lacking HAMNET support even though we have HAMNET connectivity at our location.

In order to control it through a web page, we decided to upgrade it with pinouts from the DTMF panel. These pinouts will be translated in an ARDUINO Mega 1280 which has a sufficient number of GPIOs, and the Arduino is supposed to provide a serial interface to the relay.

The serial interface will be controlled again through a Raspberry Pi that does all the rest.

The most remarkable feature is a shell like interface that allows to invoke commands and provides a history function.

We decided to implement the relay logic in the Raspberry and use the Arduino only as a GPIO swithing interface module, since relay functions are subject to change, and these changes are easier to modify than controller code.

The Arduino board also has a LM75 I2C temperature sensor mounted, so it seemed reasonable to support a query function.

2 Useful Links

3 Libraries

In my first attempt I tried to scale the T (=Taste) commandset to T1..TD including * and #, but I found out that it failed to support more than T1 - T6 commands, T7 was no longer reachable, for whatever reason. Deeper diagnostics into the library code seemed not efficient, so I decided to try another library.

The first library I used was this: https://github.com/basilfx/Arduino-CommandLine

The second library I tried gave me way better results: https://github.com/fakufaku/CmdArduino

Adding commands was easy.

4 Connectivity

We have a 37 pin cable coming down from the relay controller into my Arduino Mega 1280. The 1280 has a sufficient number of GPIOs, so that's fine. Unfortunately we have a small twister, so two cables are not wired correctly. No matter, we can compensate that in the code...

The DTMF keyboard that needs to be emulated, is controlled through four lines:

  • Bit1
  • Bit2
  • Bit4
  • Bit8

Accordingly 16 keys can be addressed, while the last key (D) equals 0000 and is taken as a zero state (setback).

We have the following line matrix:

  • Serial Pin 34 <-> Bit2 <-> GPIO 50
  • Serial Pin 35 <-> Bit1 <-> GPIO 51
  • Serial Pin 36 <-> Bit4 <-> GPIO 52
  • Serial Pin 37 <-> Bit8 <-> GPIO 53

And the keyboard uses the following binary codings, with the binary string as (Bit8 - Bit4 - Bit2 - Bit1)

  • Key 1 <-> 0001
  • Key 2 <-> 0010
  • Key 3 <-> 0011
  • Key 4 <-> 0100
  • Key 5 <-> 0101
  • Key 6 <-> 0110
  • Key 7 <-> 0111
  • Key 8 <-> 1000
  • Key 9 <-> 1001
  • Key 0 <-> 1010
  • Key A <-> 1011
  • Key B <-> 1100
  • Key C <-> 1101
  • Key D <-> 0000 (Setback code! Not used!)
  • Key * <-> 1111
  • Key # <-> 1110

5 Arduino Code

NOTE: This is not ready yet. The below code just serves as a memory backup :-)

#include <Wire.h>
#include <Cmd.h>

// GPIOs fuer die entsprechenden Signale
const int Bit2 = 50;
const int Bit1 = 51;
const int Bit4 = 52;
const int Bit8 = 53;
const int signaldauer = 500;


#define SensorAdresse 0x48 // Basisadresse für ersten Temperatursensor
// Registerparameter fuer get_LM75_temperature
#define TEMP 0  // Temperaturregister anwählen

// LM75 Configuration Register Registeradresse: 1
// Bit 0: Stromsparmodus, bei 1 geht Temperatursensor in den Stromsparmodus (keine Messung, aber aktive Steuerung) Ausgang wird auch abgeschaltet
//                        bei 0 geht Temperatursensor aus dem Stromsparmodus (Messung) Ausgang wird wieder freigegeben  
// Bit 1: Interrupt Modus, bei 1 schaltet der Ausgang sowohl bei oberen als auch unteren Schwellwert ein, wird zurückgesetzt durch Auslesen des Registers
//                         bei 0 schaltet der Ausgang bei oberen Schaltpunkt ein und bei unteren aus (default 80°C / 75°C)
// Bit 2: OS-Pin bei 1 wird das Verhalten des Ausgangs invertiert, Ausgang ist eingeschalten innerhalb der Schwellwerte
//               bei 0 Ausgang schaltet bei Überschreiten der eingestellten Schwellwerte
// Bit 3 und 4: Wert 0-3, besagt wieviele Messzyklen abgewartet wird, bis Ausgang aktiv/inaktiv wird, wenn die Bedingung erfüllt ist (verhindert Flattern des Ausgangs)
// Bit 5-7 müssen 0 sein
// Byte: 7 6 5 4 3 2 1 0

// LM75 Temperatur auslesen. Device = 0-7, regx = TEMP, OBEN, UNTEN (Registerauswahl)  
double get_LM75_temperature(int device, int regx)
{
  int8_t msb;
  int8_t lsb;
  int8_t msb1;
  Wire.beginTransmission(SensorAdresse + device);
  Wire.write(regx);
  Wire.endTransmission();
  Wire.beginTransmission(SensorAdresse + device);
  Wire.requestFrom(SensorAdresse + device, 2);
  if (Wire.available()) {
     msb1 = Wire.read();
     msb = msb1 << 1; // Vorzeichenbit entfernen, verbliebener Wert ist nun doppelt so groß
     lsb = Wire.read();
  }
  // höchstes bit von lsb sagt aus, ob 0,5 Grad dazu addiert werden sollen
  lsb = (lsb & 0x80 ) >> 7; // nun ist lsb = 0 oder 1
  Wire.endTransmission();
  if (msb1 < 0x80) { // Positiver Wert?
    return double(msb + lsb)/2; // positiver Wert
  }  
  else {
    return double(msb + lsb)/2 - 128; // negativer Wert
  }  
}



void setup()
{
  Wire.begin(); 
  pinMode(Bit1, INPUT);
  pinMode(Bit2, INPUT);
  pinMode(Bit4, INPUT);
  pinMode(Bit8, INPUT);
  
  // init the command line and set it for a speed of 57600
  cmdInit(9600);
  
  // add the commands to the command table. These functions must
  // already exist in the sketch. See the functions below. 
  // The functions need to have the format:
  //
  // void func_name(int arg_cnt, char **args)
  //
  // arg_cnt is the number of arguments typed into the command line
  // args is a list of argument strings that were typed into the command line
  cmdAdd("help",   handleHelp);
  cmdAdd("temp",   handleTemp);
  cmdAdd("state",  handleState);
  cmdAdd("T1",     handleT1);
  cmdAdd("T2",     handleT2);
  cmdAdd("T3",     handleT3);
  cmdAdd("T4",     handleT4);
  cmdAdd("T5",     handleT5);
  cmdAdd("T6",     handleT6);
  cmdAdd("T7",     handleT7);
  cmdAdd("T8",     handleT8);
  cmdAdd("T9",     handleT9);
  cmdAdd("T0",     handleT0);
  cmdAdd("TA",     handleTA);
  cmdAdd("TB",     handleTB);
  cmdAdd("TC",     handleTC);
  cmdAdd("T*",     handleTStern);
  cmdAdd("T#",     handleTHash);

}

void loop()
{
  cmdPoll();
}



void handleTemp(int arg_cnt, char **args)
{
  char dataString[7]; // gelesene Temperatur als String aufbereitet: (-xx)x.x
  double temp; // gelesene Temperatur als double
  
  temp = get_LM75_temperature(0, TEMP); //(Device)Wert vom 1. Temperatursensor lesen (0-7, je nach Jumperstellung am Board, 2. Parameter wie oben definiert)
  dtostrf(temp, 4, 1, dataString); //dtostrf(floatVar, minStringWidthIncDecimalPoint, numVarsAfterDecimal, charBuf); (standard avr-libc function)

  Serial.print("Temp: ");
  Serial.println(dataString);
}

void handleState(int arg_cnt, char **args)
{
  char* relstate = "inaktiv";

  
  Serial.print("Status: ");
  Serial.println(relstate);
}

void handleHelp(int arg_cnt, char **args)
{
  Serial.println("Available commands: 'help', 'temp', 'state', 'Tx' mit x in (1234567890ABC*#).");
}

void handleT1(int arg_cnt, char **args)
{  
  digitalWrite(Bit1, HIGH);
  digitalWrite(Bit2, LOW);
  digitalWrite(Bit4, LOW);
  digitalWrite(Bit8, LOW);

  delay(signaldauer);
  
  digitalWrite(Bit1, LOW);
  digitalWrite(Bit2, LOW);
  digitalWrite(Bit4, LOW);
  digitalWrite(Bit8, LOW);

  Serial.println("Sent: 0001 (T1)");
}

void handleT2(int arg_cnt, char **args)
{  
  digitalWrite(Bit1, LOW);
  digitalWrite(Bit2, HIGH);
  digitalWrite(Bit4, LOW);
  digitalWrite(Bit8, LOW);

  delay(signaldauer);
  
  digitalWrite(Bit1, LOW);
  digitalWrite(Bit2, LOW);
  digitalWrite(Bit4, LOW);
  digitalWrite(Bit8, LOW);

  Serial.println("Sent: 0010 (T2)");
}

void handleT3(int arg_cnt, char **args)
{  
  digitalWrite(Bit1, HIGH);
  digitalWrite(Bit2, HIGH);
  digitalWrite(Bit4, LOW);
  digitalWrite(Bit8, LOW);

  delay(signaldauer);
  
  digitalWrite(Bit1, LOW);
  digitalWrite(Bit2, LOW);
  digitalWrite(Bit4, LOW);
  digitalWrite(Bit8, LOW);

  Serial.println("Sent: 0011 (T3)");
}

void handleT4(int arg_cnt, char **args)
{  
  digitalWrite(Bit1, LOW);
  digitalWrite(Bit2, LOW);
  digitalWrite(Bit4, HIGH);
  digitalWrite(Bit8, LOW);

  delay(signaldauer);
  
  digitalWrite(Bit1, LOW);
  digitalWrite(Bit2, LOW);
  digitalWrite(Bit4, LOW);
  digitalWrite(Bit8, LOW);

  Serial.println("Sent: 0100 (T4)");
}

void handleT5(int arg_cnt, char **args)
{  
  digitalWrite(Bit1, HIGH);
  digitalWrite(Bit2, LOW);
  digitalWrite(Bit4, HIGH);
  digitalWrite(Bit8, LOW);

  delay(signaldauer);
  
  digitalWrite(Bit1, LOW);
  digitalWrite(Bit2, LOW);
  digitalWrite(Bit4, LOW);
  digitalWrite(Bit8, LOW);

  Serial.println("Sent: 0101 (T5)");
}

void handleT6(int arg_cnt, char **args)
{  
  digitalWrite(Bit1, LOW);
  digitalWrite(Bit2, HIGH);
  digitalWrite(Bit4, HIGH);
  digitalWrite(Bit8, LOW);

  delay(signaldauer);
  
  digitalWrite(Bit1, LOW);
  digitalWrite(Bit2, LOW);
  digitalWrite(Bit4, LOW);
  digitalWrite(Bit8, LOW);

  Serial.println("Sent: 0110 (T6)");
}

void handleT7(int arg_cnt, char **args)
{  
  digitalWrite(Bit1, LOW);
  digitalWrite(Bit2, HIGH);
  digitalWrite(Bit4, HIGH);
  digitalWrite(Bit8, HIGH);

  delay(signaldauer);
  
  digitalWrite(Bit1, LOW);
  digitalWrite(Bit2, LOW);
  digitalWrite(Bit4, LOW);
  digitalWrite(Bit8, LOW);

  Serial.println("Sent: 0111 (T7)");
}

void handleT8(int arg_cnt, char **args)
{  
  digitalWrite(Bit1, LOW);
  digitalWrite(Bit2, LOW);
  digitalWrite(Bit4, LOW);
  digitalWrite(Bit8, HIGH);

  delay(signaldauer);
  
  digitalWrite(Bit1, LOW);
  digitalWrite(Bit2, LOW);
  digitalWrite(Bit4, LOW);
  digitalWrite(Bit8, LOW);

  Serial.println("Sent: 1000 (T8)");
}

void handleT9(int arg_cnt, char **args)
{  
  digitalWrite(Bit1, HIGH);
  digitalWrite(Bit2, LOW);
  digitalWrite(Bit4, LOW);
  digitalWrite(Bit8, HIGH);
  
  delay(signaldauer);
  
  digitalWrite(Bit1, LOW);
  digitalWrite(Bit2, LOW);
  digitalWrite(Bit4, LOW);
  digitalWrite(Bit8, LOW);

  Serial.println("Sent: 1001 (T9)");
}

void handleT0(int arg_cnt, char **args)
{  
  digitalWrite(Bit1, LOW);
  digitalWrite(Bit2, HIGH);
  digitalWrite(Bit4, LOW);
  digitalWrite(Bit8, HIGH);

  delay(signaldauer);
  
  digitalWrite(Bit1, LOW);
  digitalWrite(Bit2, LOW);
  digitalWrite(Bit4, LOW);
  digitalWrite(Bit8, LOW);

  Serial.println("Sent: 1010 (T0)");
}

void handleTA(int arg_cnt, char **args)
{  
  digitalWrite(Bit1, HIGH);
  digitalWrite(Bit2, HIGH);
  digitalWrite(Bit4, LOW);
  digitalWrite(Bit8, HIGH);

  delay(signaldauer);
  
  digitalWrite(Bit1, LOW);
  digitalWrite(Bit2, LOW);
  digitalWrite(Bit4, LOW);
  digitalWrite(Bit8, LOW);

  Serial.println("Sent: 1011 (TA)");
}

void handleTB(int arg_cnt, char **args)
{  
  digitalWrite(Bit1, LOW);
  digitalWrite(Bit2, LOW);
  digitalWrite(Bit4, HIGH);
  digitalWrite(Bit8, HIGH);

  delay(signaldauer);
  
  digitalWrite(Bit1, LOW);
  digitalWrite(Bit2, LOW);
  digitalWrite(Bit4, LOW);
  digitalWrite(Bit8, LOW);

  Serial.println("Sent: 1100 (TB)");
}

void handleTC(int arg_cnt, char **args)
{  
  digitalWrite(Bit1, HIGH);
  digitalWrite(Bit2, LOW);
  digitalWrite(Bit4, HIGH);
  digitalWrite(Bit8, HIGH);

  delay(signaldauer);
  
  digitalWrite(Bit1, LOW);
  digitalWrite(Bit2, LOW);
  digitalWrite(Bit4, LOW);
  digitalWrite(Bit8, LOW);
  
  Serial.println("Sent: 1101 (TC)");
}

void handleTStern(int arg_cnt, char **args)
{  
  digitalWrite(Bit1, HIGH);
  digitalWrite(Bit2, HIGH);
  digitalWrite(Bit4, HIGH);
  digitalWrite(Bit8, HIGH);
  
  delay(signaldauer);
  
  digitalWrite(Bit1, LOW);
  digitalWrite(Bit2, LOW);
  digitalWrite(Bit4, LOW);
  digitalWrite(Bit8, LOW);

  Serial.println("Sent: 1111 (T*)");
}

void handleTHash(int arg_cnt, char **args)
{  
  digitalWrite(Bit1, LOW);
  digitalWrite(Bit2, HIGH);
  digitalWrite(Bit4, HIGH);
  digitalWrite(Bit8, HIGH);

  delay(signaldauer);

  digitalWrite(Bit1, LOW);
  digitalWrite(Bit2, LOW);
  digitalWrite(Bit4, LOW);
  digitalWrite(Bit8, LOW);

  Serial.println("Sent: 1110 (T#)");
}

6 Raspberry Pi Code

This code gives me the same behaviour as a minicom session. It also allows to chain invocations on the commandline:

#!/usr/bin/env python

# (c) 2018 Markus Heller, M.A. DL8RDS, relix GmbH

import time
import serial
import sys
import cgi
import json
import os
from getpass import getpass
#from time import sleep

# configure the serial connections (the parameters differs on the device you are connecting to)
ser = serial.Serial(
    port='/dev/ttyACM0',
    baudrate=9600,
    bytesize=serial.EIGHTBITS,
    parity=serial.PARITY_NONE,
    stopbits=serial.STOPBITS_ONE
)

ser.isOpen()

def interaction(input):
    ser.write(input + '\r')
    ser.flush()
    time.sleep(0.8)
    out = ''
    while ser.inWaiting() > 0:
        out += ser.read(1)

    return out[len(input)+2:]

def singlecommand(input):
    print interaction(input)

def cgicommand():
    print("Content-Type: text/html\n\n")
    arguments = cgi.FieldStorage()
    command = arguments.getvalue('command')
    if command:
        interaction(command)

def loop():
    print 'Enter your commands below.'
    print 'Insert "exit" to leave the application.'
    
    while 1 :
        out=interaction("")
        if out.endswith(">> "):
            print "Comms Init successful"
            break
    print "CMD",

    while 1 :
        # get keyboard input
        input = raw_input(">> ")

        if input in ['exit', 'e', 'q']:
            ser.close()
            exit()
        else:
            out = interaction(input)[:-4]
            print out,


if __name__ == "__main__":
    if len(sys.argv) == 1:
        loop()
    else:
        for arg in sys.argv[1:]:
            singlecommand(arg)

7 Pictures

TBD