65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Arduino 的室内气象站

starIconstarIconstarIconstarIconstarIcon

5.00/5 (12投票s)

2010年2月1日

CPOL

3分钟阅读

viewsIcon

64916

downloadIcon

792

使用 Arduino 2009 和 Visual Basic 的完整气象站。

引言

在我使用 Arduino 2009 板的第一篇文章中,我描述了一个简单的温度传感器,使用 Visual Basic 进行接口。

我开发了该板和 Visual Basic 代码,以提供一个相当实用的室内气象站。

总体操作

Arduino 2009 作为一个独立的气象站。它不显示数据。它可以独立运行数月。它对传感器进行采样,直到 RAM 满。 RAM 样本存储在 SD 卡的一个扇区上。最终,一年的天气样本可以存储在 SD 卡上。

每次连接 PC 时,任何未存储的扇区都会上传到 PC 硬盘上的文件。然后可以使用 PC 显示器的所有功能进行显示。

中心思想是 PCc 拥有每个 SD 卡扇区的文件副本,并盖有记录的日期时间。

Arduino 室内气象站

程序的活动图

Arduino IDE 使用 C++,并且此活动图的实际程序很简单。

SD 卡当前扇区保存在 EEPROM 中,以便在断电或重置期间可以重复使用。 Arduino 有一个类 EEPROM,它只允许读取和写入字节。 要读取一个长整型(在 Arduino 中为 32 字节)需要一些工作

inline void ready()
{
  unsigned long lastblock = 0;  //the last block number saved in the sd card
  unsigned long tempblock = 0;
  tempblock = EEPROM.read(0);  // remember the LSB of the last saved block
  lastblock |= tempblock;
  tempblock = EEPROM.read(1);  // remember the next LSB of the last saved block
  lastblock |= tempblock << 8;
  tempblock = EEPROM.read(2);  // remember the next LSB of the last saved block
  lastblock |= tempblock << 16;
  tempblock = EEPROM.read(3);  // remember the next MSB of the last saved block
  lastblock |= tempblock << 24;
  Serial.println("ready");  //send computer the ready to reset message
  Serial.println(lastblock);  //send computer the last saved block number
  delay(10000);  //every 10 seconds 
}//end of ready

Arduino 没有用于读取和写入 SD 卡的类,所以我编写了自己的类。 这是 *。h* 文件

/* Card type: Ver2.00 or later Standard Capacity SD Memory Card
              1.0 and 2.0 GB cards purchased in 2009 work well.
   Usage:  Must have global variable. 
              volatile unsigned char buffer[512];
           Function calls.
              unsigned char error = SDCARD.readblock(unsigned long n);
              unsigned char error = SDCARD.writeblock(unsigned long n);
            error is 0 for correct operation
            read copies the 512 bytes from sector n to buffer.
            write copies the 512 bytes from buffer to the sector n.
   References: SD Specifications. Part 1. Physical Layer Simplified Specification
               Version 2.00 September 25, 2006 SD Group.
               http://www.sdcard.org
   Code examples:  http://www.sensor-networks.org/index.php?page=0827727742
                   http://www.avrfreaks.net   search "sd card"
   Operation:  The code reads/writes direct to the sectors on the sd card.
               It does not use a FAT. If the card has been formatted the
               FAT at the lowest sectors and files at the higher sectors
               can be written over.
               The card is not damaged but will need to be reformatted at 
               the lowest level to be used by windows/linux.
   Timing:  readblock or writeblock takes 44 msec.
   Improvement: Could initialize so that can use version 1 sd and hc sd.
                Instead of CMD1 need to use CMD8, CMD58 and CMD41.
*/
#ifndef SDCARD_h
#define SDCARD_h
#define setupSPI SPCR = 0x53; //Master mode, MSB first,
                              //SCK phase low, SCK idle low, clock/64
#define deselectSPI SPCR = 0x00;  //deselect SPI after read write block
#define clearSPI  SPSR = 0x00; // clear SPI interrupt bit
#define setupDDRB DDRB |= 0x2c;  //set SS as output for cs
#define selectSDCARD PORTB &= ~0x04;  //set the SS to 0 to select the sd card 
#define deselectSDCARD PORTB |= 0x04;  //set the SS to 1 to deselect the sd card 
#include "WProgram.h"
class SDCARDclass
{
public:
    unsigned char readblock(unsigned long Rstartblock);
    unsigned char writeblock(unsigned long Wstartblock);
private:
    unsigned char SD_reset(void);
    unsigned char SD_sendCommand(unsigned char cmd, unsigned long arg);
    unsigned char SPI_transmit(unsigned char data);
};//end of class SDCARDclass
extern SDCARDclass SDCARD;
#endif

因此,当我们需要保存一个数据扇区时,我们这样做

inline void lastblocksave()
{
   unsigned int e = 0;  //the error code from the sd card
   e = SDCARD.writeblock(currentblock);  //save this 256 block of integer data 
   while (e != 0)   //cant continue if sd card not working
   {
       Serial.println("writesderror");  //send computer sd card error
       Serial.println(e);  //send computer the error number
       digitalWrite(8, HIGH);  //turn led on to show sd card error
       delay(10000);  //every 10 seconds
   }//end of sd card not working
   currentblock +=1;  //go to the next block in sd card
   EEPROM.write(0,currentblock);  //write the LSB of saved block to EEPROM
   EEPROM.write(1,currentblock >> 8);  //write the next LSB of saved block to EEPROM
   EEPROM.write(2,currentblock >> 16);  //write the next LSB of saved block to EEPROM
   EEPROM.write(3,currentblock >> 24);  //write the MSB of saved block to EEPROM      
   ramaddress = 0;   //we can now start again to save samples in RAM        
}//end of sd save

PC 程序启动

PC 程序是使用 Microsoft 的 Visual Basic Express IDE 编写的。

当显示程序加载并在激活之前,我们创建一个启动窗体,其中包含所有上传数据样本的例程。

Private Sub ArduinoWeather_Load(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles MyBase.Load
    Dim f As New showstart
    f.ShowDialog()  'startup form to connect to arduino
    While My.Computer.FileSystem.FileExists(cd & "\" & last_sector)
        last_sector += 1 'find the next block
    End While   
    If My.Computer.FileSystem.FileExists(cd & "\archive") Then
        Dim st As Stream = File.Open(cd & "\archive", FileMode.Open, FileAccess.Read)
        Dim br As New BinaryReader(st)  'to get samples from stream
        Try
            Dim n As Integer = 0  'a counter
            Do
                archived(n) = br.ReadInt64()
                archivedisplay.Items.Add(archived(n))
                If archived(n) > lastblock Then lastblock = archived(n)
                n += 1  'we get the largest archive block
                If n = 100 Then Exit Do 'no more room
            Loop
        Catch ex As Exception  'exception of none left
        Finally
            br.Close()   'must close
            st.Close()
        End Try  'exit try when all read
    End If
    fill_buffers() 'get all the samples into thir buffers
    If overflow.Text = "" Then  'enable displays
        Try
            com8 = My.Computer.Ports.OpenSerialPort("com8", 9600)
            readthread = New Threading.Thread(AddressOf read)
            readthread.Start()  'thread runs for whole program 
                                'to get samples every 10 sec
        Catch ex As Exception
            comdisplay.Text = "no connection" & vbNewLine & _
                              "or" & vbNewLine & "no communication"
            display_noconnect.Enabled = True  'just use a timer to display
        End Try
    End If
End Sub

启动窗体的活动图如下所示

此启动的全部目的是确保 SD 卡上的每个扇区都以其真实的采样时间记录在硬盘驱动器上。 我启动一个线程,该线程用于上传任何所需的数据。 以下代码是该程序的核心

Dim st As Stream = File.Open(save_sd, FileMode.Create, FileAccess.Write)
Dim bw As New BinaryWriter(st)  'to send samples to stream
For i = 0 To 255  'all old samples
    bw.Write(sd_samples(i))  'send all the samples
Next  'sector stored in file
bw.Write(date_stamp.ToString("F"))  'add date to file
date_stamp = date_stamp.Add(TimeSpan.FromSeconds(850))
bw.Close()   'sends all samples to file
st.Close()
lastblock = j 'we have uploaded one sector
upload = "uploading"
Invoke(New messdelegate(AddressOf showmessage)) 'show the progress

用户将看到一个居中屏幕的窗体

接下来,在用户窗体加载中,我们确定扇区文件的数量。 然后,将存档文件存储在它们自己的文件中。 然后,我们从相关扇区文件中填充显示缓冲区。

最后,我们启动一个线程,该线程将读取将在当前显示器中显示的新数据样本。

最后,显示用户窗体

PC 程序用户窗体

显示图像在其自己的类中生成。 数据被传递到该类,然后将图像添加到用户窗体。 如果显示尽可能详细,即每个 10 秒的样本都有其自己的像素,则完整显示跨度将为 4 小时。 可以对数据进行平均,以给出每个显示跨度最多 4 周的数据。

可以偏移显示的开始时间,以允许查看 4 周数据的任何部分。 这可以在最大分辨率下进行(每样本 10 秒)。

此代码实现了该操作

Private Sub display_all()
    Try
        Dim Tinterrim_buffer(241919) As Int32  'interim buffer for temperature display
        Dim Hinterrim_buffer(241919) As Int32  'interim buffer for humidity display
        Dim Ainterrim_buffer(241919) As Int32  'interim buffer for air pressure display
        Dim Cdisplay_start_time As DateTime  'the current display start time
        Select Case True
            Case RadioButton1.Checked
                display_span = span_define(0)  '4 hours
            Case RadioButton2.Checked
                display_span = span_define(1)  '8 hours
            Case RadioButton3.Checked
                display_span = span_define(2)  '12 hours
            Case RadioButton4.Checked
                display_span = span_define(3)  '24 hours
            Case RadioButton5.Checked
                display_span = span_define(4) '2 days
            Case RadioButton6.Checked
                display_span = span_define(5)  '4 days
            Case RadioButton7.Checked
                display_span = span_define(6) '7 days
            Case RadioButton8.Checked
                display_span = span_define(7)  '2 weeks
            Case RadioButton9.Checked
                display_span = span_define(8)  '4 weeks
        End Select
        For i = 0 To 241919
            If i < last_pointer + 1 Then
                Tinterrim_buffer(241919 - i) = temp_buffer(last_pointer - i)
                Hinterrim_buffer(241919 - i) = humid_buffer(last_pointer - i)
                Ainterrim_buffer(241919 - i) = air_buffer(last_pointer - i)
            Else
                Tinterrim_buffer(241919 - i) = 999999
                Hinterrim_buffer(241919 - i) = 999999
                Ainterrim_buffer(241919 - i) = 999999
            End If
        Next
        d.display_span_time = TimeSpan.FromMinutes(240 * display_span)
        Dim number_display As Integer = _
        1440 * display_span - 1  'the width of current span
        If cursor_time + number_display < 241920 Then
            Cdisplay_start_time = display_start_time.AddDays(-28 * cursor_time / 241919)
            Dim counter As Integer = 0
            For i = 241919 - cursor_time To 0 _
            Step -display_span  'copy working to display
                Dim average = 0
                For j = 0 To display_span - 1
                    average += Tinterrim_buffer(i - j)
                Next  'straight average of the number of samples required
                d.temperature(1439 - counter) = average / display_span
                average = 0
                For j = 0 To display_span - 1
                    average += Hinterrim_buffer(i - j)
                Next  'straight average of the number of samples required
                d.humidity(1439 - counter) = average / display_span
                average = 0
                For j = 0 To display_span - 1
                    average += Ainterrim_buffer(i - j)
                Next  'straight average of the number of samples required
                d.airpressure(1439 - counter) = average / display_span
                counter += 1 'we have done one
                If counter = 1440 Then Exit For
            Next
        Else
            hasbeenoffset.Text = "selected offset out of range"
            cursor_time = 0 'reset the value
        End If
        d.display_start_time = Cdisplay_start_time
        If zoom_TH Or zoom_AR Then
            If zoom_TH Then
                d.full_temp_hum() 'expand temp humid
            Else
                d.full_air_rain()   'expand air rain
            End If
        Else  'normal display
            d.scale_temp_hum()
            d.scale_air_rain()
        End If
    Catch ex As Exception
    End Try
End Sub

用户窗体可以启动文件管理窗体

这是存档文件的代码

Private Sub archivefile_Click(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles Button5.Click
    If My.Computer.FileSystem.FileExists(cd & "\archive") Then
        Dim words() As String = archived.Text.Split(vbNewLine)
        Dim x = CLng((words.Length - 1))
        If archiveblock > CLng(words(words.Length - 2)) + 100 Then
            Dim str As Stream = File.Open(cd & "\archive", _
                                          FileMode.Append, FileAccess.Write)
            Dim bwr As New BinaryWriter(str)  'to send samples to stream
            bwr.Write(archiveblock)  'send all the samples to disk
            bwr.Close()   'sends all samples to file
            str.Close()
        Else
            MsgBox("The archived block must be at least" & vbNewLine & _
                   "one day -that is 100 bigger than last")
        End If
        Dim st As Stream = File.Open(cd & "\archive", FileMode.Open, FileAccess.Read)
        Dim br As New BinaryReader(st)  'to get samples
        archived.Text = ""
        Try
            Do
                archived.Text = archived.Text & (br.ReadInt64()) & vbNewLine
            Loop
        Catch ex As Exception  'exception of none left
        Finally
            br.Close()   'must close
            st.Close()
       End Try  'exit try when all read
    End If
End Sub
气象站的电路图

© . All rights reserved.