📜 ⬆️ ⬇️

Internet office switch

We spent in the office a second line of the Internet. Since the main one (further I will call it first) is good in speed, but it is limited by traffic. The second is a bit slower, but unlimited. During the day, the second line is almost free and gives a good speed, so it was chosen as the main one for the working day. By evening, the speed drops dramatically due to the load on the channel and you have to switch to the first one. This is not always the case, but often enough.
Therefore, the question arose in switching lines. The router works for us with a regular FreeBSD computer on board. There was no point in cramming logic of checking the speed of the channel, besides, an indication of an active connection was needed. Having configured channel switching to console commands using sh scripts in the / bin folder, we encountered two problems:

1. Access to the console is only for the admin, but it is not always in place, and it is also not very convenient to pull to change channels.
2. There is no indication of an active connection at the moment.
Therefore, it was decided to make the switch physical and provide indicators.


Project


From the currently available iron, namely the AVR Atmega 8A and FT232RL piles, it was decided to make a switch that connects to the USB port and simply changes the active channel to another with a simple toggle switch. As well as the LED shows the one that is currently selected.
The logic of the device is very simple:
Several times per second, atmega checks the status of the inputs on port C and transmits this state through the UART as an A character for the first channel and B for the second one. A converter is connected to the UART MK on the FT232RL, which sends this symbol via USB to the virtual COM port of the server. A simple daemon written on Python is running on the server, which, in the event of a channel change, performs a switch command (changing the default gateway and static routes, but this is already beyond the scope of this article).
The daemon runs with the system, so the rc script is written for it.
But first things first.
')

Training


Based on the task in the DipTrace package, the scheme was made:


U1 is FT232RL, U3 is Atmega 8.
S1 - switch toggle switch, which also switches the status of the active channel indicator LED.

Note : after the assembly I learned about the pig , which FTDI had planted for all users of their chips, so that next time I would think twice about using their products. Since it is almost impossible to distinguish the original from a fake, the best would be not to play this lottery. But since there are several chips left in stock, I decided to do them all the same. Looking ahead to say that dancing with the drivers could not be avoided, but in the end managed to get these chips to work. With FreeBSD, there were no problems, since the old driver is there.

According to this scheme, the board was divorced for the existing case:



Next came the question of how to make a fee. You can, of course, the old grandfather LUT. But there is a better way: order from the Chinese . At Habré, the ordering process was described in some detail, and in Russia too, it often began to delight with its work.
DipTrace can export to the Gerber format, so there were no problems with the order.

Assembly


A couple of weeks after receiving the parcel with the cards in the mail, you can proceed to assembly. Since the scheme is very simple, there were no problems with the assembly:





I note only that the SMD components are soldered using a solder paste and a hair dryer. So it is more convenient and it turns out much better than a soldering iron. Especially when soldering such little things as FT232.

Customization


FreeBSD has a driver for FTDI chips, so there were no connection problems. The only thing that was required was to enable the loading of the kernel module. Register in the /boot/loader.conf file:

uftdi_load="YES" 

Atmega was stitched with avrdude, patched to work with ft232, right through the USB connection.
Firmware source
 /* * net_switch.c * * Created: 09.09.2014 10:07:41 * Author: exp131 */ #define F_CPU 1000000UL //  1,   #define BAUD 2400 //  UART, 2400    #define MYUBRR F_CPU/16/BAUD-1 #include <avr/io.h> #include <avr/interrupt.h> //       ,     void ReportStatus(); //     ISR(TIMER0_OVF_vect) { ReportStatus(); //     } //    0  1,      UART   void ReportStatus() { cli(); //      unsigned char a; if((PINC & (1<<PINC0)) && (!(PINC & (1<<PINC1)))) //   0 . 1   1  0,  a = 'A'; //   else a = 'B'; // ,   //      while(!(UCSRA & (1<<UDRE))); UDR = a; sei(); //   } // void init(void) { //  UART,      2400 UCSRB = (1<<TXEN); unsigned int ubrr = MYUBRR; UBRRH = (unsigned char)(ubrr >> 8); UBRRL = (unsigned char)ubrr; // ,        . TCCR0 = (1<<CS02)|(1<<CS00); TIMSK = (1<<TOIE0); sei(); } int main(void) { //   init(); while(1) //     { } } 



As a base for the demon, I found a python class on the web. Unfortunately, the link to the source was not preserved, just give the code here.
Daemon class code
 #!/usr/bin/env python import sys, os, time, atexit from signal import SIGTERM class Daemon: def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'): self.stdin = stdin self.stdout = stdout self.stderr = stderr self.pidfile = pidfile def demonize(self): try: pid = os.fork() if pid > 0: sys.exit(0) except OSError, e: sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror)) sys.exit(1) os.chdir("/") os.setsid() os.umask(0) sys.stdout.flush() sys.stderr.flush() si = file(self.stdin, 'r') so = file(self.stdout, 'a+') se = file(self.stderr, 'a+', 0) os.dup2(si.fileno(), sys.stdin.fileno()) os.dup2(so.fileno(), sys.stdout.fileno()) os.dup2(se.fileno(), sys.stderr.fileno()) atexit.register(self.delpid) pid = str(os.getpid()) file(self.pidfile, 'w+').write("%s\n" % pid) def delpid(self): os.remove(self.pidfile) def start(self): try: pf = file(self.pidfile, 'r') pid = int(pf.read().strip()) pf.close() except IOError: pid = None if pid: message = "Pidfile %s already exists. Deamon already running?\n" sys.stderr.write(message % self.pidfile) sys.exit(1) self.demonize() self.run() def stop(self): try: pf = file(self.pidfile, 'r') pid = int(pf.read().strip()) pf.close() except IOError: pid = None if not pid: message = "Pidfile %s does not exists. Daemon is not running?\n" sys.stderr.write(message % self.pidfile) return try: while 1: os.kill(pid, SIGTERM) time.sleep(0.1) except OSError, err: err = str(err) if err.find("No such process") > 0: if os.path.exists(self.pidfile): os.remove(self.pidfile) else: print str(err) sys.exit(1) def restart(self): self.stop() self.start() def run(self): """ Need to be overriden """ 


Based on this class, a simple daemon was written that listens to the port specified in the config and executes, depending on the state of the switch, either command A or command B.
Config
 [global] port=/dev/cuaU1 rate=2400 log=/var/log/net_switch.log cmdA=/bin/vist cmdB=/bin/unico 


And here is the code for the demon itself:
Demon code
 #!/usr/bin/env python import sys, os, time, serial, ConfigParser from daemon import Daemon class NetSwitch(Daemon): def run(self): file(self.logfile, 'a+').write("Net switch started\n") while True: ser = serial.Serial(self.port, self.rate, timeout=1) x = ser.read() if not x == self.state: self.state = x if x == 'A': os.system(self.cmdA) else: os.system(self.cmdB) file(self.logfile, 'a+').write("State changed %s\n" % x) time.sleep(0.2) def loadConfig(self, configPath): try: config = ConfigParser.RawConfigParser() config.read(configPath) self.port = config.get('global', 'port') self.rate = config.getint('global', 'rate') self.logfile = config.get('global','log') self.cmdA = config.get('global', 'cmdA') self.cmdB = config.get('global', 'cmdB') self.state = 'A' return True except: return False if __name__ == "__main__": daemon = NetSwitch('/var/run/net_switch.pid') if len(sys.argv) == 2: if 'start' == sys.argv[1]: print "Usage: %s start path_to_config" % sys.argv[0] elif 'stop' == sys.argv[1]: daemon.stop() elif 'restart' == sys.argv[1]: daemon.restart() else: print "Unknown command" sys.exit(2) sys.exit(0) elif len(sys.argv) == 3: if 'start' == sys.argv[1]: configPath = sys.argv[2] if daemon.loadConfig(configPath): daemon.start() else: print "Unable to load config file\n" else: print "Usage %s start path_to_config" % sys.argv[0] else: print "Usage: %s start|stop|restart" % sys.argv[0] sys.exit(2) 


And finally, in order for the daemon to start with the system, the rc script was written:
RC script to run
 #!/bin/sh . /etc/rc.subr name=net_switch rcvar=`set_rcvar` #reading the config load_rc_config $name : ${net_switch_enable:="NO"} : ${net_switch_config:="/usr/local/etc/net_switch/config.conf"} pidfile="/var/run/net_switch.pid" command="/usr/local/sbin/${name}.py" start_cmd="start_cmd" stop_cmd="stop_cmd" restart_cmd="restart_cmd" start_cmd() { ${command} start ${net_switch_config} } stop_cmd() { ${command} stop } restart_cmd() { ${command} restart } run_rc_command "$1" 


In order for the daemon to start along with the system boot, you need to add the following line to /etc/rc.conf:

 net_switch_enable="YES" 


Conclusion


The result was a funny, but functional device. Now any employee who is closer to the server can switch the active channel in case of any problems. Also, the LEDs show which channel is currently active. It is of a different color (green and red), so that we can see from far away what kind of connection we use.
And in conclusion I want to say that I still have fees for this device, since when ordering less than 10, the price still does not change, so I ordered 10 pieces at once. It is convenient in case some board will be ruined as a result of curved hands during installation. If anyone interested in this device and there is a desire to collect something like that - write in a personal, ready to share the boards.

Source: https://habr.com/ru/post/241245/


All Articles