Έλεγχος πολύπριζου με Arduino

Στο γραφείο γύρω από τον υπολογιστή έχουμε διάφορα πραγματάκια όλα συνδεμένα μόνιμα στην πρίζα. Στον παρόν παρουσιάζω μια λύση για τον έλεγχο τους μέσα από τον υπολογιστή.

Είναι και που έχουμε το πιο φτηνό ρεύμα στην Ευρώπη, είναι που και δεν ήθελα ο δίσκος του backup να είναι μόνιμα αναμμένος, ούτε τα διάφορα χαμπάκια και φραουλίτσες :raspberry: να είναι μόνιμα ανοιχτά.

Το Hardware

To Hardware είναι χτισμένο γύρω από ένα μικρό Arduino, κάποιες ψηφιακές έξοδοι ελέγχουν τα ρελέ. Στην μια πλευρά του κουτιού οι πρίζες έχουν ρεύμα και χωρίς να έχει ρεύμα ο υπολογιστής ενώ στην άλλη όχι. Στις ταμπέλες είναι οι εντολές για να δώσει ρεύμα μια πρίζα.

Επειδή έχουμε να κάνουμε με μεγάλες τάσεις δεν δίνω τα schematics, αλλά δεν έχουν κάτι το πολύπλοκο. Αν κάποιος δεν ξέρει να κάνει την συνδεσμολογία καλύτερα να μείνει μακρυά από αυτόν τον οδηγό :innocent:.

Το Firmware

Το πρόγραμμα που τα ελέγχει είναι επίσης εξαιρετικά απλό

Arduino code
#include <Arduino.h>
#include <CommandParser.h>
#include <EEPROM.h>

typedef CommandParser<> MyCommandParser;
MyCommandParser parser;

int pins[8] = {2, 3, 4, 5, 6, 7, 8, 9};


void cmd_on(MyCommandParser::Argument *args, char *response) {
    auto port = args[0].asInt64;
    digitalWrite(pins[port], HIGH);
    strlcpy(response, "success", MyCommandParser::MAX_RESPONSE_SIZE);
}

void cmd_(MyCommandParser::Argument *args, char *response) {
  auto port = args[0].asInt64;
  digitalWrite(pins[port], HIGH);
  strlcpy(response, "success", MyCommandParser::MAX_RESPONSE_SIZE);
}

void cmd_off(MyCommandParser::Argument *args, char *response) {
    auto port = args[0].asInt64;
    digitalWrite(pins[port], LOW);
    strlcpy(response, "success", MyCommandParser::MAX_RESPONSE_SIZE);
}

void cmd_sto(MyCommandParser::Argument *args, char *response) {
    int port = (int) args[0].asInt64;
    int8_t value = (int8_t) digitalRead(pins[port]);
    EEPROM.put(port,value);
    strlcpy(response, "success", MyCommandParser::MAX_RESPONSE_SIZE);
}

void cmd_status(MyCommandParser::Argument *args, char *response) {
    for (auto i = 0; i < 8; i++) {
        Serial.print("Socket ");
        Serial.print(i, DEC);
        Serial.print(" (pin:");
        Serial.print(pins[i], DEC);
        Serial.print(") is ");
        int value = digitalRead(pins[i]);
        Serial.print(value ? "ON  " : "OFF ");
        uint8_t eval = EEPROM.read(i);
        switch (eval) {
            case 1:
                Serial.print(" Default ON");
                break;
            case 0:
                Serial.print(" Default OFF");
                break;
            default:
                Serial.print(" Default ON (unset)");
        }
        Serial.println("");
    }
    strlcpy(response, "success", MyCommandParser::MAX_RESPONSE_SIZE);
}


void setup() {
    for (auto i = 0; i < 8; i++) {
        pinMode(pins[i], OUTPUT);
        int value;
        value = EEPROM.read(i);
        switch (value) {
            case 1:
                digitalWrite(pins[i], HIGH);
                break;
            case 0:
                digitalWrite(pins[i], LOW);
                break;
            default:
                digitalWrite(pins[i], HIGH);
        }
    }

    Serial.begin(57600);
    while (!Serial);

    //Serial.println("Power Control Gizmo");
    //Serial.println("===================");
    //Serial.println("on pin, off pin, sto pin, status");

    parser.registerCommand("on", "i", &cmd_on);
    parser.registerCommand("off", "i", &cmd_off);
    parser.registerCommand("sto", "i", &cmd_sto);
    parser.registerCommand("status", "", &cmd_status);

}

void loop() {
    if (Serial.available()) {
        char line[128];
        size_t lineLength = Serial.readBytesUntil('\n', line, 127);
        line[lineLength] = '\0';

        char response[MyCommandParser::MAX_RESPONSE_SIZE];
        parser.processCommand(line, response);
        Serial.println(response);
    }
}

Υποστηρίζει τις εντολές on pin, off pin, status καθώς και μια εντολή sto pin που σώζει την κατάσταση μιας πρίζας ώστε να επανέλθει στην αποθηκευμένη κατάσταση όταν ξεκινάει ο υπολογιστής. Η επικοινωνία με τον υπολογιστή συμβαίνει μέσα από ένα καλώδιο USB που δρα ως μια απλή σειριακή πόρτα. Επειδή η τροφοδοσία γίνεται μέσα από το ίδιο καλώδιο θα έχει ρεύμα όσο το παρέχει ο υπολογιστής.

Απαιτεί την εγκατάσταση της βιβλιοθήκης CommandParser που θα πρέπει να εγκατασταθεί. Εναλλακτικά με την βοήθεια του platformio:

platformio.ini
[env:nanoatmega328]
platform = atmelavr
board = nanoatmega328
framework = arduino

lib_deps =
    uberi/CommandParser@^1.1.0

Το πρόγραμμα ελέγχου

Μπορείς να συνδεθείς με τον ελεγκτή με οποιοδήποτε σχετικό πρόγραμμα όπως το cutecomm ή το tio, αλλά αυτό δεν βολεύει για το scripting. Το παρακάτω πρόγραμμα σε Python :python: θα συνδεθεί στην σειριακή πόρτα και θα στείλει τις εντολές, χωρίς να κάνει Reset το Arduino.

#!/usr/bin/env python3
# sudo pip3 install pyserial

import serial
import sys

arduino = serial.Serial('/dev/ttyUSB0', 57600, dsrdtr=True, timeout=1)

a = sys.argv
del a[0]
command = " ".join(a)
arduino.write(command.encode())

while True:
    data_raw = arduino.readline()
    line = data_raw.decode('utf-8').rstrip()
    if 'success' in line:
        break
    if 'error' in line:
        print(line)
        break
    if len(line) > 0:
        print(line)

arduino.close()

Πλέον το πρόγραμμα για το backup θα ανοίξει τον δίσκο, θα κάνει το αντίγραφο και θα κλείσει στην συνέχεια το ρεύμα (αφού αποπροσαρτήσει τον δίσκο προφανώς) και τα δεδομένα θα είναι εκεί ασφαλισμένα, αλλά αυτό είναι μια άλλη ιστορία.

5 «Μου αρέσει»