遙距溫度紀錄儀試製

Equipment (DIY forum) - 各類自製器材、觀星小工具等

遙距溫度紀錄儀試製

文章

mwtse » 2013-02-09, 22:31

最近有需要遠距離監察溫度,所以便使用 Arduino 造了一個簡單的溫度紀錄儀。所謂醉翁之意不在酒,監察溫度只是其次,主要目的是試試 Arduino 平台在網絡上的表現,同時學習一些相關的技術。

目前掌握到的技術有:
1. 使用 TCP/IP 連接路由器
2. 從 NTP time server 取得真實時間
3. 使用溫度計晶片量度溫度
4. 使用 SD card 儲存資料
5. 建立簡單的網頁伺服器,在互聯網上可以查看實時的溫度及翻查 SD card 上的溫度記錄
6. 定時以電郵方式傳送溫度及 IP 位址至指定郵箱

在家中試用了兩星期運作正常,下周會放在目標地點作實地測試。

具體程式運作相當簡單:
使用TMP102晶片每秒一次探測溫度,每半小時把溫度存檔,每隔四小時從 NTP server 取得時間,每天以電郵把已存檔的溫度電郵至指定郵箱,每七天開一個新檔案。

目前的效果滿意,不過 Arduino 的能力有限,不能使用 SSL 連接電郵伺服器,所以傳送電郵的功能有可能受 ISP 的政策規範而不能使用,同時,不算複雜的程式已差不多用盡 Arduino 上的資源 (32k flash memory 剩下不足 1k,RAM 剩下 320 byte),若要進行多點工作,便要使用 Arduino Mega 系列的模組了。
附加檔案
DSC08343.JPG
DSC08343.JPG (91.35 KiB) 被瀏覽 10019 次
DSC08345.JPG
DSC08345.JPG (91.84 KiB) 被瀏覽 10019 次
DSC08346.JPG
DSC08346.JPG (117.18 KiB) 被瀏覽 10019 次
最後由 mwtse 於 2013-02-11, 09:31 編輯,總共編輯了 1 次。
mwtse
 
文章: 483
註冊時間: 2011-05-01, 20:09

Re: 遙距溫度紀錄儀試製

文章mwtse » 2013-02-09, 22:35

不想做 calibration,因此花數美元買一片TMP102 ,該晶片非常細小,而且甚麼也不用做只要讀出溫度便可以。
附加檔案
DSC08348.JPG
DSC08348.JPG (84.33 KiB) 被瀏覽 10018 次
mwtse
 
文章: 483
註冊時間: 2011-05-01, 20:09

Re: 遙距溫度紀錄儀試製

文章kong » 2013-02-10, 00:59

nice. would you mind to share the code too? thanks.
kong
 
文章: 48
註冊時間: 2010-09-09, 22:50

Re: 遙距溫度紀錄儀試製

文章mwtse » 2013-02-10, 10:27

代碼: 選擇全部
//Arduino 1.0+ Only
//Arduino 1.0+ Only

#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <Time.h>
#include <Wire.h>
#include <SdFat.h>
#include <SdFatUtil.h>
#define SEND_EMAIL

/* ******** NTP Server Settings ******** */
/* us.pool.ntp.org NTP server
   (Set to your time server of choice) */
//IPAddress timeServer(96, 44, 157, 90);
//IPAddress timeServer(133, 100, 9, 2 );
//IPAddress timeServer(132,163,4,103);
//IPAddress timeServer(64, 250, 177, 145);
IPAddress timeServer(128, 138, 140, 44);

/* Set this to the offset (in seconds) to your local time
   This example is GMT + 9 */
const long timeZoneOffset = 32400L; 
 
/* Syncs to NTP server every 15 seconds for testing,
   set to 1 hour or more to be reasonable */
unsigned int ntpSyncTime = 7200;     

/* ALTER THESE VARIABLES AT YOUR OWN RISK */
// local port to listen for UDP packets
const unsigned int localPort = 8888;
// NTP time stamp is in the first 48 bytes of the message
const int NTP_PACKET_SIZE = 48;     
// Buffer to hold incoming and outgoing packets
byte packetBuffer[NTP_PACKET_SIZE]; 
// A UDP instance to let us send and receive packets over UDP
EthernetUDP Udp;                   
// Keeps track of how long ago we updated the NTP server

byte mac[] = { 0x90, 0xA2, 0xDA, 0x0D,0x65, 0x89 };
IPAddress local_IP(10, 0, 1, 201 );
IPAddress dnsServer(10, 0, 1, 1 );
IPAddress gateway(10, 0, 1, 1 );
IPAddress subnet(255, 255, 255, 0 );
//IPAddress mailServer( 218, 102, 62, 136 ); // Mail server address

const int tmp102Address = 0x48;
float temperature = -273.0;

// Keeps tracks of how long ago we did something
long lastReadingTime = 0;
time_t lastFile = 0;
time_t lastEmail = 0;
time_t lastEmailAttempt = 0;
time_t ntpLastUpdate = 0;
time_t ntpLastUpdateAttempt = 0;

int email = 1; // = 1 if need to send an email
time_t lastLog = 0;

EthernetServer webServer = EthernetServer(8081);

const int chipSelect = 4;

Sd2Card card;
SdVolume volume;
SdFile root;
SdFile file;
SdFat sd;
SdFile logFile;

// store error strings in flash to save RAM
#define error(s) error_P(PSTR(s))

/*
void error_P(const char* str) {
  PgmPrint("Error: ");
  SerialPrintln_P(str);
  if (card.errorCode()) {
    PgmPrint("SD error: ");
    Serial.print(card.errorCode(), HEX);
    Serial.print(',');
    Serial.println(card.errorData(), HEX);
  }
} */

//------------------------------------------------------------------------------
/*
 * User provided date time callback function.
 * See SdFile::dateTimeCallback() for usage.
 */
void dateTime(uint16_t* date, uint16_t* time) {
  // User gets date and time from GPS or real-time
  // clock in real callback function

  // return date using FAT_DATE macro to format fields
  *date = FAT_DATE(year(lastFile), month(lastFile), day(lastFile));

  // return time using FAT_TIME macro to format fields
  *time = FAT_TIME(hour(lastFile), minute(lastFile), second(lastFile));
}

void setup(){

  Serial.begin(9600);
  // read any existing Serial data
//  while (Serial.read() >= 0) {}
//  while (Serial.read() <= 0) {}
  pinMode(10, OUTPUT);                       // set the SS pin as an output (necessary!)
  digitalWrite(10, HIGH);                    // but turn off the W5100 chip!
 
  if (!card.init(SPI_FULL_SPEED, chipSelect)) Serial.println(F("SD error"));
  if (!volume.init(&card)) Serial.println(F("Vol error"));
  if (!root.openRoot(&volume)) Serial.println(F("Root error"));
//  delay(30000); // delay 30 second to allow time to connect the board with the router etc.
  PgmPrint("\nStarting...\n");
//  Ethernet.begin(mac, local_IP); //using DHCP
  Ethernet.begin(mac, local_IP, dnsServer, gateway, subnet ); // hard coded the local IP for easier setting of port forwarding
  delay(5000); // delay one second is usually enough, but play safe

  //Try to get the date and time
  syncTime(); 
  webServer.begin();
  PgmPrint("Local IP:"); // display the local IP on serial port, useful when using DHCP
  Serial.println(Ethernet.localIP());
  Wire.begin();

/*  SDtest(); */
  SdFile::dateTimeCallback(dateTime);
}

void loop(){

  // check for a reading no more than once a second.
  if (millis() - lastReadingTime > 1000){
    lastReadingTime = millis();
    temperature = getTemperature();
//    PgmPrint("Celsius: ");
//    Serial.println(temperature);   
  }

  // listen for incoming Ethernet connections:
  listenForEthernetClients();

  // Update the time via NTP server as often as the time you set at the top
  if(now()-ntpLastUpdateAttempt > ntpSyncTime) {
    ntpLastUpdateAttempt = now();
    syncTime();
  }
  time_t t = now();
  if( lastFile==0 ) {
    lastFile = t;
    PgmPrint("Create log file at ");
    clockDisplay();
    createLogFile();
    lastLog = t;
    logTemperature();
 //   lastEmailAttempt = t;
  }
  if( ((t - lastFile > 522000L) && (hour(t)==6)) ) { // > six days 1 hour at 6 am
//    if( ((t - lastFile > 90000L) && (hour(t)==6)) ) { // > 1 day 1 hour at 6 am
    lastFile = t;
    PgmPrint("Create a new log file at ");
    clockDisplay();
    createLogFile();
  }
  if( ((t - lastLog > 60L) && ((minute(t)%30)==0)) ) { // every 30 minutes
    lastLog = t;
    logTemperature();
 }

#ifdef SEND_EMAIL

  if( (email>0) && (t - lastEmailAttempt >= 60L) ) {
    PgmPrint("Sending email (");
    Serial.print(email);
    PgmPrint(") at ");
    clockDisplay();
    lastEmailAttempt = t;
    if( sendEmail()==0 ) {
      email = -1;
      lastEmail = lastEmailAttempt;
    } else {
      email++;
    }
  }
     
  if( (hour(t)==5) && ((minute(t)%60)>30) ) {
    if( email==0 ) email = 1;
  } else {
    if( email<0 ) email = 0;
  }

#endif

       
}

float getTemperature() {
  Wire.requestFrom(tmp102Address,2);

  byte MSB = Wire.read();
  byte LSB = Wire.read();

  //it's a 12bit int, using two's compliment for negative
  int TemperatureSum = ((MSB << 8) | LSB) >> 4;

  float celsius = TemperatureSum*0.0625;
  return celsius;
}


int syncTime() {
      PgmPrint("Sync time.");
      int trys=0;
      while(!getTimeAndDate() && trys<10){
        Serial.print('.');
        trys++;
      }
      if(trys<10){
        Serial.println();
        clockDisplay();
        ntpSyncTime = 21600; // sync time every six hours
        return(1);
      }
      else{
        PgmPrint("NTP failed");
        ntpSyncTime = 1800; // if cannot connect to time server, try again after 30 minutes
        return(0);
      }
}

// Do not alter this function, it is used by the system
int getTimeAndDate() {
   int flag=0;
   Udp.begin(localPort);
   sendNTPpacket(timeServer);
   delay(1000);
   if (Udp.parsePacket()){
     Udp.read(packetBuffer,NTP_PACKET_SIZE);  // read the packet into the buffer
     unsigned long highWord, lowWord, epoch;
     highWord = word(packetBuffer[40], packetBuffer[41]);
     lowWord = word(packetBuffer[42], packetBuffer[43]); 
     epoch = highWord << 16 | lowWord;
     epoch = epoch - 2208988800 + timeZoneOffset;
     flag=1;
     setTime(epoch);
     ntpLastUpdate = now();
   }
   return flag;
}
 
// Do not alter this function, it is used by the system
unsigned long sendNTPpacket(IPAddress& address)
{
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  packetBuffer[0] = 0b11100011;
  packetBuffer[1] = 0;
  packetBuffer[2] = 6;
  packetBuffer[3] = 0xEC;
  packetBuffer[12]  = 49;
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;                 
  Udp.beginPacket(address, 123);
  Udp.write(packetBuffer,NTP_PACKET_SIZE);
  Udp.endPacket();
}
 
// Clock display of the time and date (Basic)
void clockDisplay() {
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.print(' ');
  Serial.print(day());
  Serial.print('-');
  Serial.print(month());
  Serial.print('-');
  Serial.print(year());
  Serial.println();
}
 
// Utility function for clock display: prints preceding colon and leading 0
void printDigits(int digits){
  Serial.print(':');
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

/*
void SDtest() {
  digitalWrite(10, HIGH);  // turn off the W5100 chip!

  PgmPrint("Free RAM: ");
  Serial.println(FreeRam()); 
 
  // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
  // breadboards.  use SPI_FULL_SPEED for better performance.
 
  if (!card.init(SPI_FULL_SPEED, 4)) error("Card init");
 
  // initialize a FAT volume
  if (!volume.init(&card)) error("Vol init");
 
  PgmPrint("Volume is FAT");
  Serial.println(volume.fatType(),DEC);
  Serial.println();
 
  if (!root.openRoot(&volume)) error("openRoot");
 
  // list file in root with date and size
  root.ls(LS_DATE | LS_SIZE); 
} */

#define BUFSIZ 50

void listenForEthernetClients() {
  char clientline[BUFSIZ];
  int index = 0;
 
  EthernetClient client = webServer.available();
  if (client) {
    // an http request ends with a blank line
    boolean current_line_is_blank = true;
 
    // reset the input buffer
    index = 0;
 
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
 
        // If it isn't a new line, add the character to the buffer
        if (c != '\n' && c != '\r') {
          clientline[index] = c;
          index++;
          // are we too big for the buffer? start tossing out data
          if (index >= BUFSIZ)
            index = BUFSIZ-1;
 
          // continue to read more data!
          continue;
        }
 
        // got a \n or \r new line, which means the string is done
        clientline[index] = 0;
 
        // Print it out for debugging
        Serial.println(clientline);
 
        // Look for substring such as a request to get the root file
        if (strstr(clientline, "GET / ") != 0) {
          // send a standard http response header
          client.println(F("HTTP/1.1 200 OK"));
          client.println(F("Content-Type: text/html"));
          client.println();
 
          // print all the files, use a helper to keep it clean
          client.println(F("<h2>Files:</h2>"));
          ListFiles(client, LS_SIZE);
          printTimeS(client, now());
          client.print(F(" Temperature: "));
          client.print(temperature);
          client.print(F(" degree Celcius"));
          client.println(F("<br>"));
          client.println(F("<br>"));
          client.print(F("Last NTP update at "));
          printTime(client, ntpLastUpdate );
          client.println(F("<br>"));
#ifdef SEND_EMAIL
          client.print(F("Last email sent at "));
          printTime(client, lastEmail);
          client.println(F("<br>"));
#endif
        } else if (strstr(clientline, "GET /") != 0) {
          // this time no space after the /, so a sub-file!
          char *filename;
 
          filename = clientline + 5; // look after the "GET /" (5 chars)
          // a little trick, look for the " HTTP/1.1" string and
          // turn the first character of the substring into a 0 to clear it out.
          (strstr(clientline, " HTTP"))[0] = 0;
 
          // print the file we want
          Serial.println(filename);
 
          if (! file.open(&root, filename, O_READ)) {
            client.println(F("HTTP/1.1 404 Not Found"));
            client.println(F("Content-Type: text/html"));
            client.println();
            client.println(F("<h2>File Not Found!</h2>"));
            break;
          }
 
          client.println(F("HTTP/1.1 200 OK"));
          client.println(F("Content-Type: text/plain"));
          client.println();
 
          int16_t c;
          while ((c = file.read()) > 0) {
              // uncomment the serial to debug (slow!)
              //Serial.print((char)c);
              client.print((char)c);
          }
          file.close();
        } else {
          // everything else is a 404
          client.println(F("HTTP/1.1 404 Not Found"));
          client.println(F("Content-Type: text/html"));
          client.println();
          client.println(F("<h2>File Not Found!</h2>"));
        }
        break;
      }
    }
    client.println();
    client.print(F("Available memory = "));
    client.println(FreeRam());
    // give the web browser time to receive the data
    delay(1);
    client.stop();
  }
}

void printTime(EthernetClient client, time_t t) {
  client.print(year(t));
  client.print('-');
  client.print(month(t));
  client.print('-');
  client.print(day(t));
  client.print(' ');
  client.print(hour(t));
  client.print(':');
  if( minute(t)<10 ) client.print('0');
  client.print(minute(t));
}

void printTimeS(EthernetClient client, time_t t) {
  printTime(client, t);
  client.print(':');
  if( second(t)<10 ) client.print('0');
  client.print(second(t)); 
}

void ListFiles(EthernetClient client, uint8_t flags) {
  // This code is just copied from SdFile.cpp in the SDFat library
  // and tweaked to print to the client output in html!
  dir_t p;
 
  root.rewind();
  client.println(F("<ul>"));
  while (root.readDir(&p) > 0) {
    // done if past last used entry
    if (p.name[0] == DIR_NAME_FREE) break;
 
    // skip deleted entry and entries for . and  ..
    if (p.name[0] == DIR_NAME_DELETED || p.name[0] == '.') continue;
 
    // only list subdirectories and files
    if (!DIR_IS_FILE_OR_SUBDIR(&p)) continue;
 
    // print any indent spaces
    client.print(F("<li><a href=\""));
    for (uint8_t i = 0; i < 11; i++) {
      if (p.name[i] == ' ') continue;
      if (i == 8) {
        client.print('.');
      }
      client.print( (char) p.name[i] );
    }
    client.print(F("\">"));
 
    // print file name with possible blank fill
    for (uint8_t i = 0; i < 11; i++) {
      if (p.name[i] == ' ') continue;
      if (i == 8) {
        client.print('.');
      }
      client.print((char)p.name[i]);
    }
 
    client.print(F("</a>"));
 
    if (DIR_IS_SUBDIR(&p)) {
      client.print('/');
    }
 
    // print modify date/time if requested
    if (flags & LS_DATE) {
       root.printFatDate(p.lastWriteDate);
       client.print(' ');
       root.printFatTime(p.lastWriteTime);
    }
    // print size if requested
    if (!DIR_IS_SUBDIR(&p) && (flags & LS_SIZE)) {
      client.print(' ');
      client.print(p.fileSize);
    }
    client.println(F("</li>"));
  }
  client.println(F("</ul>"));
}

void logTemperature() {
  if( openLogFile()<0 ) {
    Serial.print(F("Error writing log file."));
    return;
  }
  // if the file opened okay, write to it:
  Serial.print(F("Writing to log file..."));

  logFile.print(year(lastLog));
  logFile.print('-');
  logFile.print(month(lastLog));
  logFile.print('-');
  logFile.print(day(lastLog));
  logFile.print(' ');
  if( hour(lastLog) < 10 ) logFile.print(' ');
  logFile.print(hour(lastLog));
  logFile.print(':');
  if( minute(lastLog) < 10 ) logFile.print('0');
  logFile.print(minute(lastLog));
  logFile.print(F("   Celcius: "));
  logFile.println(temperature);
 
  // close the file:
  logFile.close();
  Serial.println(F("done."));
}

void createLogFile() {
  if( openLogFile()<0 ) {
    Serial.print(F("Error creating log file."));
    return;
  }
  // if the file opened okay, write to it:
//  Serial.print(F("Creating log file..."));
  logFile.println(F("Temperature record"));

  // close the file:
  logFile.close();
//  Serial.println(F("done."));
}

int openLogFile() {
  digitalWrite(10, HIGH);   // turn off the W5100 chip!

  if (!sd.begin(chipSelect, SPI_FULL_SPEED)) return(-1);

  char filename[13];
  int y = year(lastFile);
  filename[0] = (y / 1000) + '0';
  y = y - (y/1000)*1000;
  filename[1] = (y / 100) + '0';
  y = y - (y/100)*100;
  filename[2] = (y / 10) + '0';
  filename[3] = (y % 10) + '0';
  filename[4] = (month(lastFile) / 10) + '0';
  filename[5] = (month(lastFile) % 10) + '0';
  filename[6] = (day(lastFile) / 10) + '0';
  filename[7] = (day(lastFile) % 10) + '0';
  filename[8] = '.';
  filename[9] = 'T';
  filename[10] = 'X';
  filename[11] = 'T';
  filename[12] = 0;
  // open the file for write at end like the Native SD library
  if (!logFile.open(filename, O_RDWR | O_CREAT | O_AT_END)) {
    return(-1);
  }
  return(0);

}

#ifdef SEND_EMAIL

EthernetClient emailClient;

int sendEmail() {
  boolean reply = false;
  char ip[16];
  // your email server
  if (emailClient.connect("smtp.yourISP.com", 25)) {
    Serial.println(F("Connected"));
    emailClient.println(F("HELO yourname.com"));
    if ( emailClient.find("250") ) {
      if( emailClient.find("[") ) {
        reply = true;
        int i=0;
        char c = emailClient.read();
        while (emailClient.available() && (c!=']') && (i<15)) {
          ip[i] = c;
          i++;
          c = emailClient.read();
        }
        ip[i] = 0;
      }
    }
    emailClient.find("you");
    // replace ************ with your base64 encoded password
    emailClient.println(F("AUTH PLAIN ************"));
    SMTP_Ack();
    emailClient.println(F("MAIL FROM:<yourname@yourISP.com>"));
    SMTP_Ack();
    emailClient.println(F("RCPT TO:<receipient@yyy.com>"));
    SMTP_Ack();
    emailClient.println(F("RCPT TO:<another_receipient@yahoo.com>"));
    SMTP_Ack();
    emailClient.println(F("DATA"));
    emailClient.println(F("From: <sender@xxx.com>"));
    emailClient.println(F("To: Receipient Name <receipient@yyy.com>"));
    emailClient.println(F("Subject: Temperature Report"));
    emailClient.println(); // empty line required
    if( reply == true ) {
      emailClient.print(F("Browse http://"));
      emailClient.print(ip);
      // I use 8081 to access the temperature sensor
      emailClient.println(F(":8081 for details."));
    }
/*    emailClient.print(F("Available memory = "));
    emailClient.println(FreeRam()); */
    if( email>1 ) {
      emailClient.print(F("Retried "));
      emailClient.print(email-1);
      emailClient.println(F(" times"));
    }
    emailClient.println();
 
    char filename[13];
    int y = year(lastFile);
    filename[0] = (y / 1000) + '0';
    y = y - (y/1000)*1000;
    filename[1] = (y / 100) + '0';
    y = y - (y/100)*100;
    filename[2] = (y / 10) + '0';
    filename[3] = (y % 10) + '0';
    filename[4] = (month(lastFile) / 10) + '0';
    filename[5] = (month(lastFile) % 10) + '0';
    filename[6] = (day(lastFile) / 10) + '0';
    filename[7] = (day(lastFile) % 10) + '0';
    filename[8] = '.';
    filename[9] = 'T';
    filename[10] = 'X';
    filename[11] = 'T';
    filename[12] = 0;
   
    if (! file.open(&root, filename, O_READ)) {
      emailClient.println(F("Unable to read log file"));
      Serial.println(F("Unable to read log file"));
    } else {
      int16_t c;
      while ((c = file.read()) > 0) {
        emailClient.print((char)c);
      }
      file.close();
    }
    delay(1);
    emailClient.println();
    emailClient.println('.');
    SMTP_Ack();
    emailClient.println(F("QUIT"));
  } else {
    Serial.println(F("Connection to mail server failed"));
    emailClient.stop();
    return(-1);
  }
  SMTP_Ack();
 
  if (!emailClient.connected()) {
    Serial.println(F("Disconnecting."));
    Serial.println();
    emailClient.stop();
  }
  return(0);
}

void SMTP_Ack()
{
  delay(300);
  while(emailClient.available()) {
    char ACK = (char)emailClient.read();
    Serial.print(ACK);
  }
}

#endif

mwtse
 
文章: 483
註冊時間: 2011-05-01, 20:09

Re: 遙距溫度紀錄儀試製

文章mwtse » 2013-02-10, 10:30

See also this
http://bildr.org/2011/01/tmp102-arduino/#

for connection of TMP102
mwtse
 
文章: 483
註冊時間: 2011-05-01, 20:09

Re: 遙距溫度紀錄儀試製

文章社長 » 2013-02-10, 14:44

謝謝分享,這控制版的確開發了無限可能,剛看過原來有不少選項:

http://arduino.cc/en/Main/Products

我的第一個想法是可以解決很多馬達驅動需求,赤道儀、木板赤道儀、Timelapse拍攝平台等,加上網絡功能,可以想像這在遠程觀測上的功效及診斷可行性。

期望更多分享 :D
兩個天文台 - 白鷺湖天文台、西藏自動化天文觀測站
主鏡 - Officina Stellare 500 口徑f3.8 RiFast攝星儀、APM/TMB 254 f9 APO、SkyWatcher Dob 18、305 f8.5 牛頓鏡、Paramount ME 赤道儀
配件 - 2 full sets of Supermonocentrics、Baader Mark V Binoviewer、Ethos、Canon 5D Mark II Mod、FLI Proline 16803冷凍相機、Mercedes SUV
星河科研社 http://www.astro.hk 電郵 saviofong@astro.hk
頭像
社長
Site Admin
 
文章: 12686
註冊時間: 2010-02-01, 14:17
來自: 香港

Re: 遙距溫度紀錄儀試製

文章mwtse » 2013-02-11, 09:48

也不要對 Arduino 平台有過高的期望,這平台簡單易用,而且因為開放源碼,所以有非常多的程式庫可以選擇使用,但由不同人編寫的程式庫獨立來使用沒有問題,可是一旦合併起來想做點真正有用的工作,便會產生許多相容性問題,因此網上有人索性只用 Arduino 的硬件,自行以 gcc 編譯直接上載程式。不過,軟件上的問題其實是可以解決的,但就不要太輕視所需要的時間。
mwtse
 
文章: 483
註冊時間: 2011-05-01, 20:09

Re: 遙距溫度紀錄儀試製

文章mwtse » 2013-02-11, 10:02

有關寄發電郵方面,是使用 telnet 連接電郵伺服器的 port 25,例如 netvigator 便是 smtp.netvigator.com:25
這些伺服器需要登入才能使用,登入名字及密碼需要以 base64 的格式傳送。我試過使用轉碼網頁,但不成功。最後發現使用 Mac 機非常簡單,只要在命令提示視窗中打入 perl 指令便可以,在 Windows 系統中我就不曉得如何做了,可能需要建立一個 perl server。

Find your authentication information

In order to use the AUTH command, you need to know the base64-encoded version of the userid and password you will be using to authenticate to the server. Normally this would be the same as the userid and password you would use to check your mail using IMAP or POP3. This perl command (which requires the MIME::Base64 module) will do the encoding for you:

% perl -MMIME::Base64 -e 'print encode_base64("\000jms1\@jms1.net\000not.my.real.password")'
AGptczFAam1zMS5uZXQAbm90Lm15LnJlYWwucGFzc3dvcmQ=

Note: Make sure to use \0 both as the first character of what you're encoding, and as the separator between the userid and the password. There was an error with the original version of these directions- I had forgotten about needing a \0 at the beginning. Sorry all!
Another reader pointed out that perl silently interprets the "@" sign in the middle of a string and replaces it with the contents of an array with that name, if one exists... or with nothing, if not. I just did a full two-way test with my real password, and it turns out if you don't put a backslash in front of the "@" sign it won't work. Good call.
And JT Justman pointed out that if you use \0 as the separator, and the userid or password happens to start with a digit, perl will try to find and use a three-digit octal character code instead of a one-digit null byte with two normal digits behind it. Using \000 instead of just \0 prevents this from happening.


See: http://qmail.jms1.net/test-auth.shtml for detail
mwtse
 
文章: 483
註冊時間: 2011-05-01, 20:09

Re: 遙距溫度紀錄儀試製

文章kong » 2013-02-11, 16:45

I would use arduino as measurement transmitters only, and Rasberry Pi as central controller or webserver...
kong
 
文章: 48
註冊時間: 2010-09-09, 22:50

Re: 遙距溫度紀錄儀試製

文章社長 » 2013-02-11, 23:31

mwtse 寫:也不要對 Arduino 平台有過高的期望,這平台簡單易用,而且因為開放源碼,所以有非常多的程式庫可以選擇使用,但由不同人編寫的程式庫獨立來使用沒有問題,可是一旦合併起來想做點真正有用的工作,便會產生許多相容性問題,因此網上有人索性只用 Arduino 的硬件,自行以 gcc 編譯直接上載程式。不過,軟件上的問題其實是可以解決的,但就不要太輕視所需要的時間。


我想自己不會做太複雜的"人工智能運算",能實現一些以前用純用簡單電路做不來的功能已很不錯,尤其之前聽你提到這模組時,我沒留意其網絡及USB等連接功能,現在是完全另一想法了。

我現時白鷺湖天文台的馬達控制器也用微控制器及自己編寫程式製作出來的,按理也能做到絶大部份我想在天文應用裡要實現的一些功能,但整片電路每一顆零件、走線都要從零開始,萬一出現什麼問題,或日後要維修就痛苦了,正如我現在重看廿年前在大學時寫的C++程式,感覺是外星語言一樣,很難想像當時是如何寫出來的 :oops:

關於數據上傳方面,因為不太懂,很粗疏的想法,定時儲存在電腦上然後用.bat檔定時ftp到網頁上又如何? :roll:
兩個天文台 - 白鷺湖天文台、西藏自動化天文觀測站
主鏡 - Officina Stellare 500 口徑f3.8 RiFast攝星儀、APM/TMB 254 f9 APO、SkyWatcher Dob 18、305 f8.5 牛頓鏡、Paramount ME 赤道儀
配件 - 2 full sets of Supermonocentrics、Baader Mark V Binoviewer、Ethos、Canon 5D Mark II Mod、FLI Proline 16803冷凍相機、Mercedes SUV
星河科研社 http://www.astro.hk 電郵 saviofong@astro.hk
頭像
社長
Site Admin
 
文章: 12686
註冊時間: 2010-02-01, 14:17
來自: 香港

Re: 遙距溫度紀錄儀試製

文章mwtse » 2013-02-20, 23:27

遙距溫度紀錄儀已運作了超過一星期,TXT 檔每半小時記錄溫度一次,每隔七天開一個新的檔案。沒有使用電郵,因此可用記憶體增加至 386 位元組。

http://110.74.119.129:8081
附加檔案
Screen Shot 2013-02-20 at 11.26.37 PM.png
Screen Shot 2013-02-20 at 11.26.37 PM.png (44.12 KiB) 被瀏覽 9888 次
mwtse
 
文章: 483
註冊時間: 2011-05-01, 20:09

Re: 遙距溫度紀錄儀試製

文章mwtse » 2013-02-20, 23:39

社長 寫:
關於數據上傳方面,因為不太懂,很粗疏的想法,定時儲存在電腦上然後用.bat檔定時ftp到網頁上又如何? :roll:


我一直對電腦有一點偏見:由於零部件太多,所以出毛病的機會較高。如果可能,我傾向儘量用 MCU 處理所有工作,一來工作壽命長,二來就算壞掉了,整片更換花不了多少錢。
mwtse
 
文章: 483
註冊時間: 2011-05-01, 20:09

Re: 遙距溫度紀錄儀試製

文章社長 » 2013-02-22, 01:42

mwtse 寫:
社長 寫:
關於數據上傳方面,因為不太懂,很粗疏的想法,定時儲存在電腦上然後用.bat檔定時ftp到網頁上又如何? :roll:


我一直對電腦有一點偏見:由於零部件太多,所以出毛病的機會較高。如果可能,我傾向儘量用 MCU 處理所有工作,一來工作壽命長,二來就算壞掉了,整片更換花不了多少錢。


精簡的確是工程設計的一項重要考慮,Paramount ME 面世時其中一項改動,就是減省了30%電子零件,我上次入藏也減省了相若的連線。

不過在某些程況下,我還是會直接用手提電腦的,例如我在西藏現時第二套Boltwood Cloud Sensor便是用一台7年前買、之前跌爆了顯示屏的Thinkpad X60s作操控,這些電腦在二手市場找可能也是數百元,但運算功能很強。我甚至不排除,日後西藏主控機會用手提電腦去做,至少在我的環境裡,原來比工控機還要穏定,唯一是硬盤有機會出現高山反應,改用SSD才可避免問題。
兩個天文台 - 白鷺湖天文台、西藏自動化天文觀測站
主鏡 - Officina Stellare 500 口徑f3.8 RiFast攝星儀、APM/TMB 254 f9 APO、SkyWatcher Dob 18、305 f8.5 牛頓鏡、Paramount ME 赤道儀
配件 - 2 full sets of Supermonocentrics、Baader Mark V Binoviewer、Ethos、Canon 5D Mark II Mod、FLI Proline 16803冷凍相機、Mercedes SUV
星河科研社 http://www.astro.hk 電郵 saviofong@astro.hk
頭像
社長
Site Admin
 
文章: 12686
註冊時間: 2010-02-01, 14:17
來自: 香港


回到 儀器 - 自製器材專區

誰在線上

正在瀏覽這個版面的使用者:沒有註冊會員 和 8 位訪客