Отправляем емейл из Perl с авторизацией и html

Tags:

Отправляем письмо в html с авторизацией из Perl.

use strict;
use Net::SMTP;
use Authen::SASL; #нужен для авторизации

use utf8;
use Encode;
use MIME::Base64


  my $message = "<html><body>Тестовое письмо</body></html>";

# авторизационные данные
my $smtp_host = 'smtp.server.address';
my $smtp_user = 'user_name';
my $smtp_pass = 'password';


my $debug = 1; # рассматриваем процесс подключения в деталях

# mail properties
my $mail_from = 'user@example.com';
my $mail_to = 'recipient@example.com';
my $mail_subject = 'Тестовое письмо';

# Выставляем заголовки. Сюда можно добавить еще всякого при необходимости
my $mail_headers = "From: $mail_from\n".
"To: $mail_to\n".
"Subject: ".encode('MIME-Header',$mail_subject)."\n".
"MIME-Version: 1.0\n".
"Content-type: text/html; charset=UTF-8\n".
"Content-Transfer-Encoding: base64\n\n";

my $mail_body = $message;

# Отправляем письмо
my $smtp = Net::SMTP->new($smtp_host, Debug => $debug) or die "cannot connect to server";
$smtp->auth($smtp_user,$smtp_pass) or die "could not authenticate";
$smtp->mail($mail_from);
$smtp->to($mail_to);
$smtp->data();
$smtp->datasend($mail_headers);
$smtp->datasend(encode_base64(encode('UTF-8', $mail_body)));
$smtp->dataend();
$smtp->quit;

При необходимости можно добавить файлы, PGP подписи и так далее.

Добавляем второй экструдер к RAMPS 1.4/Marlin

Tags:

В прошлой серии мы установили прошивку Marin на Arduino Mega+RAMPS 1.4 и откалибровали все это дело. Теперь пришло время добавить второй экструдер.

Для этого нам понадобится:

  • второй экструдер
  • второй хот-энд в сборе и все прилегающие детали, типа трубки боудена
  • каретка на два экструдера. Я использую такую.

В общем случае все просто идем в Arduino IDE и правим конфигурацию.

Меняем конфигурацию платы управления на RAMPS 1.4 c 2 экструдерами и подогреваемым столом

#ifndef MOTHERBOARD
  #define MOTHERBOARD BOARD_RAMPS_14_EEB
#endif

Указываем кол-во экструдеров:

#define EXTRUDERS 2

Указываем тип датчика температуры для второго экструдера:

#define TEMP_SENSOR_1 1

Максимальную температуру:

#define HEATER_1_MAXTEMP 250

Если надо, устанавливаем направление вращения мотора:

#define INVERT_E1_DIR true

В целом, если у вас одинаковые моторы на обоих экструдерах, то на этом настройка закончена.

Но если у моторы разные или на одном экструдере у меня мотор с шестерней подачи диаметром 10мм, а на втором - 7мм, то тут начинаются проблемы с калибровкой.

Как откалибровать второй экструдер? Идем в пронтерфейс и говорим:

T1

Эта команда указывает что сейчас активен экструдер номер 2 (отсчет начинается с 0). Включаем подачу филамента, для подачи будет использоваться число шагов из прошивки, т.е. для первого экструдера. Смотрим разницу и с помощью пропорции получаем кол-во шагов для активного экструдера. У меня из-за разницы в подающих шестернях получилось 80 шагов на мм.

Говорим:

M92 E80

Этим мы устанавливаем новое кол-во шагов для текущего экструдера. Проверяем, при необходимости подгоняем значение.

После калибровки вылезает проблема - Marlin не поддерживает установку разного числа шагов для разных экструдеров, соответственно в прошивке нам эти данные не сохранить. Придется этот вопрос решать програмно.

Напишем небольшой фильтр которым будем обрабатывать готовый g-code файл.

#!/usr/bin/env perl

use strict;

my $T0 = 'M92 E95';
my $T1 = 'M92 E80';

local $^I = '.bak';
while (<>) {
    s/^T0/T0\n$T0/;
    s/^T1/T1\n$T1/;
    print;
}

Этот скрипт ищет команду T и заменяет ее нужным нам значением. В данном случае добавляет команду M92 E. Скрипт получает на вход имя файла, перебирает его построчно и в каждой строке заменяет совпадения на то что мы указали. Оригинальный файл сохраняется с расширением .bak.

Дальше нам надо задать смещение экструдеров

Для Slic3r идем в Printer Settings -> Extruder 2 и выставляем смещение. Примерное смещение можно получить если измерив расстояние между соплами, однако программое расстояние может отличаться от физического расстояния, так что все равно придется заняться калибровкой.

Для этого печатаем деталь для калибровки 2х экструдеров и точно подгоняем все для идеальной работы.

Настраиваем прошивку Marlin для Arduino + RAMPS 1.4

Tags:

Так получилось что некоторое время назад я стал владельцем китайского клона Prusa i3 под гордым китайским названием Hesine M505. Это чудо китайской мысли конечно было далеко от совершенства, но с задачей печатать все подряд справлялось вполне успешно. Однако, чем дальше в лес - тем толще партизаны. И забравшись в лес подальше возжелал я печатать детали с использованием растворимых поддержек, да и двумя цветами печатать тоже было бы неплохо.

К сожалению, у родной для принтера платы Melzi V2 был фатальный недостаток - на ней отсутсвовал порт для подключения второго экструдера. Изучение матчасти показало, что можно взять еще одну такую же плату и воткнуть ее в режиме слейва. Однако ценник на это удовольствие получался высоковатым. И, что самое неприятно, при таком апгрейде терялась возможность подключения экрана и кнопок управления. Т.е. печатать можно будет только с компьютера, а это не самая лучшая идея по многим причинам.

Так я пришел к решению полностью заменить мозги принтера. Выбор был сделан в пользу нестареющей класски Arduino Mega 2560 + RAMPS 1.4 + A4988. Быстро сказка сказывается, да долго посылочка едет. Получив посылку с мозгами, моторами и прочим полезным в хозяйстве инвентарем я обнаружил что забыл заказать шестерню податчика экструдера. Благо на тот момент она уже ехала ко мне с али, где я заказал ее просто так, от жадности.

В общем, пока необходимые запчасти едут, можно подключить и отстроить новые мозги в конфигурации с одним экструдером, а потом просто переконфигурировать прошивку когда будет собрано все железо.

Ставить мы будем классику жанра - Marlin. Клонируем репозиторий в любое удобное место отсюда https://github.com/MarlinFirmware/Marlin. Скачиваем Arduino IDE.

В Arduino IDE открываем прошивку. Выбираем нашу плату Arduino Mega 2560 и процессор AtMega 2560.

Дальше нас интересует вкладка с файлом Configuration.h, теперь мы будем его безудержно править.

Выбираем нашу плату: RAMPS 1.4 с одним хотэндом

#ifndef MOTHERBOARD
  #define MOTHERBOARD BOARD_RAMPS_14_EFB
#endif

В файле boards.h приведен полный список поддерживаемых плат и их вариаций. Нас пока интересует только RAMPS 1.4 с одним экструдером.

Выставляем кол-во температурных сенсоров. Сенсоров должно быть по числу экструдеров. В нашем случае 1. Убеждаемся что напротив первого сенсора в списке стоит 1.

#define TEMP_SENSOR_0 1

Выставляем максимальную температуру хотэнда и стола.

#define HEATER_0_MAXTEMP 250
#define BED_MAXTEMP 130

У меня стоят термопредохранители на 252 градуса, так что максимальная температура хота должна быть ниже температуры срабатывания предохранителя. Если собираетесь печатать чем-то вроде поликарбоната - то температуру надо поднять. Со столом таже история, единственное что даже печать нейлоном не требует очень больших температур стола, 130 градусов должно хватить всем.

В Hesine M505 стоят нормально замкнутые концевики, а в прошивка по умолчанию рассчитывает на нормально разомкнутые. Включаем инвертирование концевиков:

// Mechanical endstop with COM to ground and NC to Signal uses "false" here (most common setup).
#define X_MIN_ENDSTOP_INVERTING true // set to true to invert the logic of the endstop.
#define Y_MIN_ENDSTOP_INVERTING true // set to true to invert the logic of the endstop.
#define Z_MIN_ENDSTOP_INVERTING true // set to true to invert the logic of the endstop.

Проверяем направление вращения моторов. Тут история примерно таже что и с концевиками, т.к. прошивка изначально рассчитана на Ultimaker.

// Invert the stepper direction. Change (or reverse the motor connector) if an axis goes the wrong way.
#define INVERT_X_DIR true
#define INVERT_Y_DIR true
#define INVERT_Z_DIR false

И для экструдера повторяем операцию.

#define INVERT_E0_DIR true

Выставляем размеры рабочей зоны

#define X_MIN_POS 0
#define Y_MIN_POS 0
#define Z_MIN_POS 0
#define X_MAX_POS 250
#define Y_MAX_POS 230
#define Z_MAX_POS 140

Для начала можно выставить заводомо большие чем рабочая зона габариты. Потом все откалибруется по месту. По умолчанию в прошивке стоят значени 200х200х200, тогда как у Hesine M505 рабочее поле по всем направлениям больше чем эти цифры. И прошивка просто не даст двигаться за их пределы.

Устанавливаем координаты начала стола. Это нужно чтобы ноль стола в слайсере совпадал с нулем в координатах принтера. Иначе модель может вылезать за пределы зоны печати.

#define MANUAL_X_HOME_POS -30
#define MANUAL_Y_HOME_POS -20

Выставляем шаги для моторов.

#define DEFAULT_AXIS_STEPS_PER_UNIT   {100,100,1600,95}

Параметры тут следующие: мотор_Х, мотор_Y, мотор_Z, мотор_экструдера

Для рассчета шагов по X и Y используем следующую формулу:

(200*16)/(16*2)=80 шагов

Где 200 - это число шагов двигателя на 360 градусов. Типичная цифра для моторов с шагом 1.8 градуса. 16 в числителе - кол-во микрошагов на шаг. 16 в знаменателе - кол-во зубов на шпуле. 2 - стандартный шаг для ремня GT2

Для Z:

3200/2 = 1600

В Hesine M505 штатно используется трапециидальный винт с шагом резьбы 2мм. Соответственно мы белим число шагов на полный оборот на кол-во миллиметров которые будут пройдены за оборот и получаем число шагов на миллиметр.

Подачу экструдера пока посчитаем и выставим предварительно. Потом ее все равно придется подгонять. Шестерня экструдера штатно имеет диаметр около 10мм. Получаем длинну окружности на один оборот: 3.14*10=31.4 мм на оборот.

Делим число шагов на длинну окружности и получаем число шагов на миллиметр.

3200/31.4=101.9

округляем до 102.

Заливаем все это дело в Arduino.

После того как все залилось и все железо подключено запускаем Pronterface и начинаем калибровку.

  • Двигаем моторы и проверяем что они крутятся в нужную сторону. Если это не так - меняем параметр INVERT_*_DIR на противоположное значение.
  • Проверяем состояние концевиков. По команде M119 будет показано состояние концевиков. Если концевик нажат - напротив него должно быть написано TRIGGERED. Напротив ненажатого - open. Если это не так - меняем настройку *MINENDSTOP_INVERTING.
  • Даем команду G28. Все оси должны приехать в свое минимальное положение. Дальше через Pronterface двигаем все оси в их безопасное максимальное положение и даем комаду M114. Она покажет текущее положение по осям. Вносим эти данные в настройку *MAXPOS.
  • Опять говорим G28. Передвигаем экструдер в нулевую координату стола по X-Y. Смотрим что показывает M114 и эти цифры переносим в MANUAL_*HOMEPOS с обратным знаком. Т.е. если M114 говорит что координата по Х сейчас 30 - то в настройку пишем -30. Это значит что после ухода в HOME экструдер отъедет от него на 30мм и будет считать это положение нолем.
  • откручиваем сопло или трубку боудена у экструдера. На прутке отмечаем расстояние, например 10 см, и прогоняем эту же длинну из Pronterface. После этого смотрим сколько в реальности прошло прутка и корректируем кол-во шагов для экструдера чтобы он прогонял четко нужное кол-во пластика. Важно понимать что на горячую с установленным соплом эта цифра все равно будет отличаться от установленного. Если вы всегда печатаете соплом одного диаметра - можно повторить эту операцию с соплом и полученные данные внести в прошивку. Я использую несколько сопел, так что объем пластика корректирую через настройку потока в слайсере.

После того как все это сделано - печатаем тестовую модель. Я использую пустой куб 20х20х20 мм. После того как он отпечатан - проверяем что размеры сторон у нас точно соответсвуют тому что должно быть. Если это не так по отдельным измерениям - корректируем кол-во шагов для нужной оси.

На этом основные настройки закончены. Дальше можно тюнить приведенные выше параметры для более качественной печати.

Работаем с самбой из Java с помощью JCIFS

Tags:

Простые примеры работы с самбой при помощи JCIFS

Ниже показан пример класса для копирования файла на самба-шару и просмотра содержимого заданной шары

import jcifs.Config;
import jcifs.smb.*;
import java.io.*;

public class SambaTest {
  // Нормальный конструктор я тут делать не буду, для тестового примера это не нужно
static final String USER_NAME = "username";
static final String PASSWORD = "password";
static final String DOMAIN = "user_domain";

// Путь к сетевой папке с которой будем работать
static final String NETWORK_FOLDER = "smb://server/share/";


// Копируем файл на шару.
// К сожалению SmbFile ничего не знает о методе copyTo из File,
// так что придется этот метод эмулировать руками. Халява не прокатила :(
// В обратную сторону все тоже самое, только потоки будут в обратную сторону.
public boolean copyFileToSamba(String srcFilePath, String destPath) {
    boolean successful = false;
    try{
        // Создаем объект аутентификатор
        NtlmPasswordAuthentication auth =
          new NtlmPasswordAuthentication(DOMAIN, USER_NAME, PASSWORD);

        // Читаем содержимое исходного файла
        File srcFile = new File(srcFilePath);
        InputStream localFile = new FileInputStream(srcFile);

        // Создаем объект для потока куда мы будем писать наша файл
        SmbFileOutputStream destFileName = new SmbFileOutputStream(
          new SmbFile(destPath+File.separator+srcFile.getName(), auth));

        // Ну и копируем все из исходного потока в поток назначения.
        BufferedReader brl = new BufferedReader(
          new InputStreamReader(localFile));
        String b = null;
        while((b=brl.readLine())!=null){
            destFileName.write(b.getBytes());
        }
        destFileName.flush();


        successful = true;
    } catch (Exception e) {
        successful = false;
        e.printStackTrace();
    }
    return successful;
}

// Читаем
public boolean readShareContent() {
    boolean successful = false;
    try{
        // Создаем объект для аутентификации на шаре
        NtlmPasswordAuthentication auth =
          new NtlmPasswordAuthentication(DOMAIN, USER_NAME, PASSWORD);
        String path = NETWORK_FOLDER;

        // Ресолвим путь назначения в SmbFile
        SmbFile baseDir = new SmbFile(path, auth);

        // Вычитываем все содержимое шары в массив
        SmbFile[] files = baseDir.listFiles();

        // Делаем что-нибудь со списком файлов
        for (int i = 0; i < files.length; i++) {
          SmbFile file = files[i];
          if (file.isDirectory()) {
                System.out.println("Is DIR: "+file.toString());
                continue;
          } else {
                System.out.println("Is FILE: "+file.toString());
          }
        }

        successful = true;
    } catch (Exception e) {
        successful = false;
        e.printStackTrace();
    }
    return successful;
  }
}

Если все работы с шарой невероятно тупят - то нужно сменить режим ресолвинга сервера и шары.

В конструктор добавляем следующее:

Config.setProperty( "jcifs.resolveOrder", "DNS");

Этим мы указываем что ресолвить имя сервера мы будем ТОЛЬКО через DNS. Можно туда добавить всякие NetBIOS и прочее, но на практике я с необходимостью это делать не сталкивался. Естественно, что если у вас в сети нет локального DNS сервера который будет ресолвить именя локальным машин - то механизм надо сменить на другой. Выбор механизмов ресолвинга будет проходить в порядке указанном в конфиге.

Если предполагается копирование больших бинарных файлов, то код копирования надо заменить с:

BufferedReader brl = new BufferedReader(new InputStreamReader(localFile)); String b = null; while((b=brl.readLine())!=null){ destFileName.write(b.getBytes()); }

на:

byte[] buffer = new byte[1024000]; int noOfBytes = 0;

while ((noOfBytes = localFile.read(buffer)) != -1) { destFileName.write(buffer, 0, noOfBytes); }

Если этого не сделать - то большие файлы будут копироваться криво.

Installing and configuring Postgresql 9.4 on Debian 8

Tags:

Install the postgresql server package:

#apt-get install postgresql-9.4

Create cluster (if it haven't created automatically):

#pg_createcluster 9.4 main --start

Next we need to allow connects from remote sources.

Edit /etc/postgresql/9.4/main/postgresql.conf and set listen_addresses to '*'.

Restart the Postgresql service:

#service postgresql Restart

Now we should create database user.

#su postgres
#psql

postgres=# CREATE USER db_user_name WITH PASSWORD 'secret_password';

Create new database and grant access to it to our user.

postgres=# CREATE DATABASE "db_name"
  WITH OWNER "db_user_name"
  ENCODING 'UTF8'
  LC_COLLATE = 'en_US.UTF-8'
  LC_CTYPE = 'en_US.UTF-8'
  TEMPLATE = template0;

Now we have database with UTF8 collation, it is time to allow connect from remote sources. Our user is the owner of the schema.

Edit /etc/postgresql/9.4/main/pg_hba.conf and add line

host    all   db_user_name    0.0.0.0/0               md5

after the line:

host    all   all             127.0.0.1/32            md5

Save file and restart the service.

Now we can connect to our server from remote computer.