2-Kanals USB Templogger

Berätta om dina pågående projekt.
Nickeh
Inlägg: 82
Blev medlem: 9 augusti 2006, 00:41:37
Ort: Linköping
Kontakt:

2-Kanals USB Templogger

Inlägg av Nickeh »

Jag sitter och knopar på en templogger som jag skall använda för att mäta temperaturer i espresso maskiner.

En första version har jag byggt ihop som läser av 2 st k-typ termoelement och skickar datan via USB till datorn, på datorn har jag skrivit ett program som loggar datan och ritar en plot.

Hårdvaran jag använder är enligt följande:
PIC 18f2550
2 st MAX6675 A/D omvandlare

Den första versionen måste vara kopplad till datorn för att logga, den drivs av ström från USB porten och skickar data via USB. Jag har använt mig av Dangerous Prototypes USB stack, stångades med microchips USB exempel utan att lyckas. Dangerous prototypes kod var för en nybörjare lättare att förstå!

I skrivandes stund håller jag på att utveckla nästa version av loggern, jag håller på att ordna med en LCD för att kunna visa mätningar utan att vara kopplat till datorn. Tankar finns också på att addera något slags minne för att kunna logga data och sedan koppla till datorn för att tömma enheten. Där har jag funderat på antingen EEPROM eller SD kort.

Ikväll har jag suttit och bråkat med att få en hd44780 lcd att fungera, jag har kommit så långt att jag lyckas initiera den och skriva ut text men bara på första raden?
Jag kan flytta markören på första raden utan problem men när jag flyttar till 0xC0 vilket borde peka på rad 2 kol 1 kommer det ingen text.
Vrider jag upp kontrasten så är det bara mörk bakgrund på översta raden så det känns som att displayen initieras i fel läge helt enkelt.

Bifogar min kod här så kanske någon kan se vad jag inte lyckats se de timmar jag suttit och letat :)

LCD funktionerna jag skrivit:

Kod: Markera allt


void E_strobe(void)
{
    E_PIN=1;
    Delay10Us(10);
    E_PIN=0;
}

void lcd_pordelay(void){
        DelayMs(15);
        DelayMs(15);
}
void lcd_goto(unsigned char pos)
{
        lcd_busy();
        RS_PIN = 0;
	lcd_write(0x80|pos);
}

void lcd_write(unsigned char c)
{
        RS_PIN = 0;
        lcd_busy();                         // Wait if busy
        LCD_DATA = ( ( c >> 4 ) & 0x0F );   // Hi NIB
        E_strobe();
        Delay10Us(5);
        LCD_DATA = (  c  & 0x0F );          // Low NIB
        E_strobe();
}

void lcd_putch(unsigned char c)
{
        RS_PIN = 1;
        LCD_DATA &= 0xf0;                     // Nolla data portar
        LCD_DATA |= ( ( c >> 4 ) & 0x0F );   // Hi NIB protect d4-d5
        E_strobe();

        LCD_DATA &= 0xf0;                     // Nolla data portar
        LCD_DATA |= (  c  & 0x0F );          // Low NIB
        E_strobe();
}

void lcd_clear(void)
{
        lcd_busy();
	RS_PIN = 0;
	lcd_write(0x1);
}
void lcd_puts(const char * s){
	while(*s)
		lcd_putch(*s++);
}
void lcd_busy(void)
{
    char RS_state;
    RS_state = RS_PIN;          /* Save rs state*/
    LATA = 0;
    TRISA = 0x08;
    RW_PIN = 1;                 /* Set control pins*/
    RS_PIN = 0;
    //Delay10Us(5);
    E_PIN = 1;                  /* Ask for data*/
    //Delay10Us(5);

    while(BF_PIN);              /* Wait for BF*/

    E_PIN = 0;
    TRIS_BF = 0;                /* BF pin as out */
    RW_PIN = 0;                 /* Back to write */
    RS_PIN = RS_state;          /* Reset RS to value before calling*/
    //Delay10Us(2);
}
void InitLCD(void)
{
    	char init_value;
	init_value = 0x03;
        
        TRISB =0;

	RS_PIN = 0;
	E_PIN = 0;
        RW_PIN = 0;

        lcd_pordelay();

        LCD_DATA = init_value;      // 8 bit fs
        E_strobe();
	DelayMs(5);

        E_strobe();                 // 8 bit fs
        Delay100Us(2);

        E_strobe();                 // 8bit fs
        Delay100Us(2);

	LCD_DATA = 0x2;                 // Four bit mode
        E_strobe();
        lcd_busy();                     // Wait until not busy

	lcd_write(0x28);                // Function Set 4-bit, 2 line
        //b00101000
        lcd_write(0x08);                // Display Off
        //b00001000
        lcd_write(0x01);                // Clear display
        //b00000001
        lcd_write(0x06);                // Entry mode, incremental, no shift
        //b00000100
        lcd_write(0x0C);                // Display On, cur off, blink off
        //b00001100
}

LCDn körs i 4 bitars läge och jag använder busy flag för att låta displayen köra klart instruktionerna.
InitLCD anropas från min main varpå displayen startar och jag kan skriva text med lcd_puts('sträng'), lcd_goto() funkar som den skall men bara på rad 1.

Kod: Markera allt

void main(void)
{
    int i;
    SetupBoard(); //setup the hardware, customize for your hardware
    InitLCD();
    DelayMs(21);
    DelayMs(21);

    lcd_goto(0x00);
    lcd_puts("Sensor 1:    0");
    lcd_putch(0xDF);    // Degrees
    lcd_putch(0x43);    // C
    lcd_goto(0x40);
    lcd_puts("Sensor 2:    0");
    lcd_putch(0xDF);    // Degrees
    lcd_putch(0x43);    // C
.
.
.
.
.
Bifogar lite bilder också, på bilden av prototyp nr 2 syns problemet med displayen.

All input är välkommen! Någon som har provat på att använda SD kort och EEPROM? vad är enklast, jag vill mäta ganska korta förlopp och en mätning kräver ~200 kb i de textfiler jag nu skapar på datorn.
Du har inte behörighet att öppna de filer som bifogats till detta inlägg.
Senast redigerad av Nickeh 5 juni 2013, 12:24:34, redigerad totalt 1 gång.
Användarvisningsbild
JimmyAndersson
Inlägg: 26577
Blev medlem: 6 augusti 2005, 21:23:33
Ort: Oskarshamn (En bit utanför)
Kontakt:

Re: 2-Kanals USB Templogger

Inlägg av JimmyAndersson »

"lcd_write(0x28);"

När man skriver hexadecimalt sådär så blir det svårare att felsöka, jämfört med om du skrivit ut det med ettor och nollor:
lcd_write(b00101000);
Då kunde man ha jämfört med displayens datablad och direkt sett om allt var som det skulle.


Men iochmed att displayens första rad visar rätt,
så innebär det nästan säkert att du helt enkelt har initierat den som 1-rads.
Var är raden som ställer in att displayen ska initiera 2 rader?
(Jag missar den nog eftersom du som sagt skrivit i hex-format i stället för bin.)
Användarvisningsbild
lizerdboy
Inlägg: 1610
Blev medlem: 6 oktober 2003, 22:24:12
Ort: Stockholm

Re: 2-Kanals USB Templogger

Inlägg av lizerdboy »

snyggt,
Vad programmerar du i för språk på datorn ? undrar vilket plot du använder dig av ?
Nickeh
Inlägg: 82
Blev medlem: 9 augusti 2006, 00:41:37
Ort: Linköping
Kontakt:

Re: 2-Kanals USB Templogger

Inlägg av Nickeh »

JimmyAndersson skrev:"lcd_write(0x28);"

När man skriver hexadecimalt sådär så blir det svårare att felsöka, jämfört med om du skrivit ut det med ettor och nollor:
lcd_write(b00101000);
Då kunde man ha jämfört med displayens datablad och direkt sett om allt var som det skulle.


Men iochmed att displayens första rad visar rätt,
så innebär det nästan säkert att du helt enkelt har initierat den som 1-rads.
Var är raden som ställer in att displayen ska initiera 2 rader?
(Jag missar den nog eftersom du som sagt skrivit i hex-format i stället för bin.)
Det är just 0x28 som sätter antal rader.
Så jag skickar b 0 0 1 0 1 0 0 0.

Ur hd44780 databladet:
Function set 0 0 0 0 1 DL N F — — Sets interface data length, (DL), number of display lines (N), and character font (F).
https://www.sparkfun.com/datasheets/LCD/HD44780.pdf
Ur databladet för just min lcd:
Function Set 0 0 0 0 1 DL 1 0 * * Set interface data length (DL).
http://www.channel-microelectronic.de/c ... pec_r1.pdf
Så de ger värdet på N och F för just min display, alltså 2 rader, 5x8 font size
lizerdboy skrev:snyggt,
Vad programmerar du i för språk på datorn ? undrar vilket plot du använder dig av ?
Skriver i C#, med oxyplot. Tog ett tag att komma igång med men jag gillar verkligen resultatet med oxyplot, snygga grafer som går att konfigurera väldigt fritt.
http://oxyplot.codeplex.com/

Kan lägga upp källkod om det finns intresse men det är inget mästerverk, det är mitt första C# program så det är nog mycket dumheter i den.
Men den lyckas iaf. öppna com porten skicka komandon till loggern och läsa svaren, så om det finns intresse kan jag gå igenom koden och göra den lite mer läsbar och lägga upp den.
Användarvisningsbild
lizerdboy
Inlägg: 1610
Blev medlem: 6 oktober 2003, 22:24:12
Ort: Stockholm

Re: 2-Kanals USB Templogger

Inlägg av lizerdboy »

Bra jobbat :tumupp:

Jag var bara nyfiken, har själv gjort ett liknande program som jag använder till det mesta numera, se http://lizerd.se/?page_id=137
Finns även en tråd om den här på EF, Edit** http://elektronikforumet.com/forum/view ... =2&t=54104
Där så kör jag Zedgraph, men jag håller på med Oxyplot i ett par andra program som jag skriver nu, super simpelt att komma i gång.

Keep up the good work :tumupp:
Nickeh
Inlägg: 82
Blev medlem: 9 augusti 2006, 00:41:37
Ort: Linköping
Kontakt:

Re: 2-Kanals USB Templogger

Inlägg av Nickeh »

Trevligt lizerdboy! Lite större skala på ditt projekt, imponerande!

[EDIT] Hittade felet med LCDn nu, togglade min E pinne bara en gång när jag läser busy flaggan. La till en toggel i lcd_busy() så kom det igång!
Nu är det bara att få LCDn att funka samtidigt som allt annat, lite nervös att USB interruptsen skall störa mina rutiner.
Användarvisningsbild
Nisse
Inlägg: 908
Blev medlem: 9 juli 2006, 23:25:46
Ort: Kumla

Re: 2-Kanals USB Templogger

Inlägg av Nisse »

Nickeh skrev:Kan lägga upp källkod om det finns intresse men det är inget mästerverk, det är mitt första C# program så det är nog mycket dumheter i den. Men den lyckas iaf. öppna com porten skicka komandon till loggern och läsa svaren, så om det finns intresse kan jag gå igenom koden och göra den lite mer läsbar och lägga upp den.
Jag skulle gärna se källkoden. :) Har tankar kring ett projekt där detta skulle passa bra. Kan lite grundläggande C#, men comportar har jag lite sämre koll på för att inte tala om grafer.

Hälsningar
Nisse
Nickeh
Inlägg: 82
Blev medlem: 9 augusti 2006, 00:41:37
Ort: Linköping
Kontakt:

Re: 2-Kanals USB Templogger

Inlägg av Nickeh »

Nisse skrev: skulle gärna se källkoden. :) Har tankar kring ett projekt där detta skulle passa bra. Kan lite grundläggande C#, men comportar har jag lite sämre koll på för att inte tala om grafer.

Hälsningar
Nisse
Då ska jag kolla på det i helgen och lägga ut koden!
Nickeh
Inlägg: 82
Blev medlem: 9 augusti 2006, 00:41:37
Ort: Linköping
Kontakt:

Re: 2-Kanals USB Templogger

Inlägg av Nickeh »

Nisse skrev:
Nickeh skrev:Kan lägga upp källkod om det finns intresse men det är inget mästerverk, det är mitt första C# program så det är nog mycket dumheter i den. Men den lyckas iaf. öppna com porten skicka komandon till loggern och läsa svaren, så om det finns intresse kan jag gå igenom koden och göra den lite mer läsbar och lägga upp den.
Jag skulle gärna se källkoden. :) Har tankar kring ett projekt där detta skulle passa bra. Kan lite grundläggande C#, men comportar har jag lite sämre koll på för att inte tala om grafer.

Hälsningar
Nisse
Här kommer koden som den ser ut idag, inte 100% stabilt, lite felhantering som måste läggas till.
Kolla i private void btnStart_Click för större delen av det som har med serieporten att göra!
Kommer att jobba igenom koden och postar väl en ny version när jag är nöjd med den men nu ligger fokus på att få hårdvaran att fungera som jag vill!

Om någon har tips på förbättringar så tas de gärna emot, är som sagt helt grön på C# programmering så det finns säkert mycket att kommentera på!

Kod: Markera allt

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO.Ports;
using System.Timers;
using System.Windows.Threading;


namespace PIC_TEMP_BLEND
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        //Serial Port definition
        static SerialPort serialPort1 = new SerialPort();

        public string filename;
        public int ActSensorNo;
        public bool SensNo1;
        public bool SensNo2;

        OxyPlot.Wpf.LineSeries lineSeries1;
        List<TimeValue> dataSensor1;
        
        OxyPlot.Wpf.LineSeries lineSeries2;
        List<TimeValue> dataSensor2;
        
        //Timer definition
        private System.Threading.Timer sendTimer;
        DateTime startTime;
        


        
        public MainWindow()
        {             
            InitializeComponent();

            update_combolist();

            //Initialize Plot
            dataSensor1 = new List<TimeValue>();
            lineSeries1 = new OxyPlot.Wpf.LineSeries();
            lineSeries1.ItemsSource = dataSensor1;
            lineSeries1.DataFieldX = "Time";
            lineSeries1.DataFieldY = "Value";

            dataSensor2 = new List<TimeValue>();
            lineSeries2 = new OxyPlot.Wpf.LineSeries();
            lineSeries2.ItemsSource = dataSensor2;
            lineSeries2.DataFieldX = "Time";
            lineSeries2.DataFieldY = "Value";

            plotTemperature.Series.Add(lineSeries1);
            plotTemperature.Series.Add(lineSeries2);


            Execute.InitializeWithDispatcher();
        }


        private void btnStart_Click(object sender, RoutedEventArgs e)
        {
            if (!serialPort1.IsOpen)
            {
                if (comboPorts.SelectedValue!= null)
                {
                    int ReadTimerInterval = 500;
                    bool problem = false; 
                    //Open Port
                    serialPort1.PortName = comboPorts.SelectedValue.ToString();
                    serialPort1.BaudRate = 9600;
                    try
                    {
                        serialPort1.Open();
                    }
                    catch (Exception a)
                    {
                        MessageBox.Show("Couldn't open port " + serialPort1.PortName + ", error message: \n\n" + a.ToString(),"Can't open port");
                        problem = true;
                    }
                    if (!problem)
                    {
                        //Create event handler
                        serialPort1.DataReceived += new SerialDataReceivedEventHandler(serialPort1_DataRecieved);

                        //Create new time
                        if (chkSens1.IsChecked.Value == true && chkSens2.IsChecked.Value == true)
                            ReadTimerInterval = ReadTimerInterval / 2;
                        sendTimer = new System.Threading.Timer(sendTimer_task, null, 0, ReadTimerInterval);
                        startTime = DateTime.Now;

                        //Update Buttons
                        btnStart.Content = "Stop";
                        btnReset.IsEnabled = false;
                        btnSave.IsEnabled = false;

                        //Make sure we create new filename for new readings
                        filename = DateTime.Now.ToString("yyyy-MM-dd_HH_mm_ss");

                        // Clear Plot
                        dataSensor1.Clear();
                        dataSensor2.Clear();
                        Execute.OnUIThread(() => plotTemperature.RefreshPlot(true));

                        //What sensors to read
                        SensNo1 = chkSens1.IsChecked.Value;
                        SensNo2 = chkSens2.IsChecked.Value;
                        if (chkSens1.IsChecked == true && chkSens2.IsChecked == true)
                            ActSensorNo = 1;
                        else if (chkSens2.IsChecked == true)
                            ActSensorNo = 2;
                        else
                            ActSensorNo = 1; 

                        

                    }

                }
                else
                {
                    MessageBox.Show("Select Witch COM Port to use", "Error", MessageBoxButton.OK);
                }
            }
            else
            {
                //Stop Recording and close port update button
                if( serialPort1.IsOpen )
                    serialPort1.Close();
                
                sendTimer.Dispose();

                btnStart.Content = "Start";
                btnReset.IsEnabled = true;
                btnSave.IsEnabled = true;

            }

        }

        //sendTimer Event
        private void sendTimer_task(object state)
        {

            try
            {
                if (ActSensorNo == 1)
                {
                    serialPort1.Write("1");     // Tell PIC to read temperature

                }
                else if (ActSensorNo == 2)
                {
                    serialPort1.Write("2");

                }
            }
            catch (Exception a)
            {

                sendTimer.Dispose();

                MessageBox.Show("Couldn't send to port" + serialPort1.PortName + ", error message: \n\n" + a.ToString(), "Can't send to port");
                if (serialPort1.IsOpen)
                    serialPort1.Close();

                Execute.OnUIThread(() => btnStart.Content = "Start");
                Execute.OnUIThread(() => btnReset.IsEnabled = true);
                Execute.OnUIThread(() =>  btnSave.IsEnabled = true);
            }

        }

        private void serialPort1_DataRecieved(object sender, SerialDataReceivedEventArgs e) // Data recieved to port
        {
            TimeSpan elapsed = DateTime.Now - startTime;    //Create timestamp
       
  
            
            string recieved_Data="";

            if(serialPort1.IsOpen)
                recieved_Data = serialPort1.ReadExisting();  //Get data from buffer
            if (recieved_Data == "" || recieved_Data == "??")
                recieved_Data = "0";

                    
            //Update Plot
            //TimeValue 
            data = new TimeValue { Time = elapsed, Value = (Convert.ToDouble(recieved_Data))/4};


            if (ActSensorNo == 1)
                dataSensor1.Add(data);
            else
                dataSensor2.Add(data);
            
            Execute.OnUIThread(() => plotTemperature.RefreshPlot(true));

            if (SensNo1 == true && SensNo2 == true && ActSensorNo == 2)
                ActSensorNo = 1;
            else if (SensNo1 == true && SensNo2 == true && ActSensorNo == 1)
                ActSensorNo = 2;
            

        }

        private void Window_Closed(object sender, EventArgs e)          // Cleanup
        {
            if (serialPort1.IsOpen)
                serialPort1.Close();
        }
       
        class TimeValue
        {
            public TimeSpan Time { get; set; }
            public double Value { get; set; }
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {

         

            string message_box_text = "";
            if (chkLogfile.IsChecked == true)
            {

                string txtfilename = filename + ".txt";
                try
                {

                    using (System.IO.StreamWriter file = new System.IO.StreamWriter(txtfilename, true))
                    {
                        file.WriteLine("Sensor1");
                        foreach (TimeValue i in dataSensor1)
                        {
                            string line = i.Time.ToString() + "," + i.Value.ToString();
                            file.WriteLine(line);
                        }
                        file.WriteLine("Sensor2");
                        foreach (TimeValue i in dataSensor2)
                        {
                            string line = i.Time.ToString() + "," + i.Value.ToString();
                            file.WriteLine(line);
                        }
                        message_box_text = "Log saved \n";
                    }
                }
                catch (System.IO.IOException a)
                {
                    MessageBox.Show(e.ToString());
                    message_box_text = ""; 
                }
            }
            if (chkImage.IsChecked == true)
            {
                string imgfilename = filename + ".jpg";
                plotTemperature.SaveBitmap(imgfilename, 800, 600);
                
                message_box_text = message_box_text + "Image saved"; 
            }
            if (chkImage.IsChecked == false && chkLogfile.IsChecked == false)
                MessageBox.Show("Nothing selected to save", "Select image or log");
            else
                MessageBox.Show(message_box_text,"Files saved");
        }

        private void btnReset_Click(object sender, RoutedEventArgs e)
        {
            dataSensor1.Clear();
            dataSensor2.Clear();
            Execute.OnUIThread(() => plotTemperature.RefreshPlot(true));
            btnSave.IsEnabled = false;

        }

        private void comboPorts_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (comboPorts.SelectedValue == "Update List")
            {
                update_combolist();
            }

        }

        private void update_combolist (){
                // Initialize Combobox
                // Get a list of serial port names. 
                string[] ports = SerialPort.GetPortNames();
                //Ad to combobox
                comboPorts.Items.Clear();
                foreach (string port in ports)
                {
                    comboPorts.Items.Add(port);
                }
                comboPorts.Items.Add("Update List");

        }

        
    }
    public static class Execute
    {
        private static Action<System.Action> executor = action => action();

        /// <summary>
        /// Initializes the framework using the current dispatcher.
        /// </summary>
        public static void InitializeWithDispatcher()
        {

            var dispatcher = Dispatcher.CurrentDispatcher;

            executor = action =>
            {
                if (dispatcher.CheckAccess())
                    action();
                else dispatcher.BeginInvoke(action);
            };
        }

        /// <summary>
        /// Executes the action on the UI thread.
        /// </summary>
        /// <param name="action">The action to execute.</param>
        public static void OnUIThread(this System.Action action)
        {
            executor(action);
        }
    }
}
Skriv svar