Hemautomation V2

Berätta om dina pågående projekt.
Användarvisningsbild
squiz3r
Inlägg: 5424
Blev medlem: 5 september 2006, 20:06:22
Ort: Lund
Kontakt:

Hemautomation V2

Inlägg av squiz3r »

I mitt förra hemautomationsprjekt styrde jag mina trådlösa NEXA kontakter med en Raspberry Pi och en PIC med radiosändare samt en app till telefonen. Det hela fungerade hyfsat med var lite ruffigt byggt så jag tänkte börja om från scratch nu.

Jag började med att köpa en ny Raspberry Pi, denna gången modell 2 som har fyrkärnig processor tillsammans med en hel del andra uppgraderingar. Köpte den på Kjell för att få den som är tillverkad i UK. Finns att tillgå på DX men det är bara en 50-lapp som skiljer så det priset kan det vara värt.
P_20160204_214906.jpg
Innan de intressanta delarna kan börja så skall jag få en order från DX med arduino, radiosändare och lite annat skoj. Under tiden började jag med belysningen i mitt vardagsrum. Jag har för tillfället två lampor, en plafond och en takkrona men båda går på samma brytare. Detta löste jag genom att sätta trådlösa nexa mottagare på dem.

Började med julas billiga mottagare för vägguttag. Plockade ur kretskortet och skruvade fast i en kopplingsdosa från biltema. Helt perfekt passform. Efter ett tag blev det dock trassligt med alla sladdar och jag bestämde mig för att överge denna designen och övergå till en dimmer för takkronan.
P_20160204_205405.jpg
En fjärrdimmer från Anslut för 49kr på jula samt en fjärrströmbrytare från NEXA (169kr) införskaffades. Båda för fast installation. Dessa skruvades sedan fast på toppen av plafonden (som förövrigt också är Jula, 49kr). Nexas brytare var betydligt tunnare så jag satte en plastbit som distans så att de fick samma höjd. Sedan räcker det med en lång skruv från lampan in i taket så ligger lampan platt på fjärrströmbrytarna.
P_20160204_194853.jpg
Lite kabeldragning...
P_20160204_201754.jpg
På plats i taket
P_20160204_202641.jpg
Väggströmbrytaren skruvades loss och kablarna skarvedes i en skarvdosa.
P_20160204_204238.jpg
Behöver bara slipa och lägga på ny färg runt om..
P_20160204_204355.jpg
Äntligen kan jag ha lite stämningsbelysning i taket.
P_20160204_210856_NT.jpg
Mer kommer förhoppningsvis snart.
Du har inte behörighet att öppna de filer som bifogats till detta inlägg.
Användarvisningsbild
squiz3r
Inlägg: 5424
Blev medlem: 5 september 2006, 20:06:22
Ort: Lund
Kontakt:

Re: Hemautomation V2

Inlägg av squiz3r »

Än har det inte hänt något tyvärr... Jag beställde lite delar från DX som sagt. Beställde från europe direct eftersom de skulle leverera på 5-7 dagar vilket kändes OK. Än har jag dock inte fått något eftersom allt tydligen var semesterstängt från 1-14 feb (kinesiskt nyår antar jag). Detta innebär att det kommer ta 19-21 dagar istället innan jag får mina delar. Inte lika kul..

Jag kopplade i alla fall in min nya Raspberry Pi 2 idag och installerade Raspian för att se att allt fungerade.
P_20160211_190427_HDR.jpg
Testade även så att Java fungerade. På senare år finns det ju äkta stöd för java i Raspian, rätt smidigt. Måste dock installera Eclipse eller nått, utvecklingsmiljöerna som var med var allt annat än kompetenta. Bara att vänta nu tills min WiFi adapter kommer.
P_20160211_193615.jpg
Du har inte behörighet att öppna de filer som bifogats till detta inlägg.
G-man
EF Sponsor
Inlägg: 1368
Blev medlem: 21 december 2005, 20:04:36
Ort: Lkpg

Re: Hemautomation V2

Inlägg av G-man »

Kul projekt! Jag kör en Raspberry med FRXCom och Domoticz, har fullt upp ändå med att testa enheter och så, har en hel del prylar från Kina nu som funkar ihop med FRXCom med.

2 nyfikna frågor!

När du har kört 2st devicer brevid varandra som i lådan där, får du inte problem med att den bommar ofta då? Jag kan hemma inte ha 2 brytare i samma grenuttag ens utan att det krånglar.. samma sak med både tellstick jag hade och RFXCom..

Du valde Raspberry som plattform.. jag har själv funderat lite på att det kanske hade varit bättre med något som inte har ett minneskort som media att köra ifrån.. de har ju en begränsad livslägnd.. kortare än en SSD eller vanlig HDD. Har du kört såna här saker så pass länge på raspberry så att man kan avfärda det som ett ickeproblem? Det är ju en del skrivade och läsande från kortet när den suger in data från som i mitt fall 8tempgivare 4 fuktgivare.. elmätare.. vattenmätare osv..
Användarvisningsbild
Electricguy
Inlägg: 12307
Blev medlem: 15 augusti 2007, 16:52:14
Ort: Kälmä' typ..

Re: Hemautomation V2

Inlägg av Electricguy »

Relaterat:
qx5
Inlägg: 1678
Blev medlem: 14 augusti 2014, 04:23:04

Re: Hemautomation V2

Inlägg av qx5 »

Flashminne är en jättebra sak om man utgår från dess egenskaper. Vilket innebär att man undviker att genomföra upprepade skrivoperationer. Under unix bör man t.ex slå av uppdatering av "atime" i filsystemet. Men bäst är om man kan skrivskydda filsystemet som ligger på flashminne och använda disk i RAM för temporära filer. Loggning sparas tillfälligt i ett batteribackat RAM och skrivs sedan till flash så fort ett helt block har fyllts. Man kan t.om ordna en rå-partition där man kan ordna så att block för block fylls utan ett filsystem som försöker att uppdatera en massa metadata.

En annan fördel som kan uppnås med lite optimering av detta slag är att känsligheten för abrupt avslut blir mindre då filsystemet inte hamnar i odefinierat läge och sekvensiell skrivning kan ordnas som en logg.
Användarvisningsbild
squiz3r
Inlägg: 5424
Blev medlem: 5 september 2006, 20:06:22
Ort: Lund
Kontakt:

Re: Hemautomation V2

Inlägg av squiz3r »

G-man: Jag har inte haft några problem med att de inte reagerar på kommandon men däremot en annan konstig sak.. För några dagar sedan började timern slås av när jag trycker på knappen för att slå av den andra strömbrytaren.. Detta är ju rätt lustigt med tanke på att jag inte alls har haft strömmen bruten eller tryckt på några inlärningsknappar. Tycker mig ha upplevt detta tidigare också på de lösa uttagen men avfärdade det med att jag måste ha glömt att jag hade en gammal kod inlagd i dem.

Vad gäller SD kortets livslängd vet jag faktiskt inte riktigt.. Jag funderade på detta rätt mycket förra gången jag gjorde min hemautomation eftersom jag då loggade temperatur direkt till minneskortet. Denna gången tänkte jag ladda upp det till en databas på mitt webhotell istället. På så sätt slipper jag upprepade skrivningar till minneskortet och jag slipper även köra Raspberryn som server. Förra gången hade jag en del problem med dynIP. Jag kollade runt på internet en del och trots hur många det är som har sina Raspberries ståendes på under långa perioder så hittade jag inte ett enda fall där någon faktiskt kunde bekräfta att minneskortet var slut, bara mycket spekulerande i att det skulle kunna hända. Men visst, där är väll inte fel att undersöka det som qx5 skriver. En annan metod om man inte är väldigt rädd om mätdatan skulle väl kunna vara att samla den i arbetsminnet tills man har kommit upp i storleken av en sektor och först då skriva allt till minneskortet. Tidigare har xxargs här på forumet skrivit:
Modernare SD/USB-minnen så sprids skrivningarna på alla block i tur och ordning så att alla block slits jämt (vilket innebär att redan skriven data flyttas också fysiskt och reallokeras till samma adress som tidigare)

Dock moderna multinivå-celler som finns i dagens många GB-minnen kanske tål runt 1000 omskrivningar (mot tidigare 10000 för dubbelnivå-celler och singelnivåceller 100000 ggr) innan någon cell i blocket börja koda fel - i början upptäcks och rättas av ECC med reallokering till andra lediga minnesblock och reservblock men till sist så har man kört igenom samtliga block och allt fler håller inte data och man börja få läsfel.

Hur det beter sig utåt mot OS om man kommer så långt är okänt då man i flesta fallen har tjänat ut utrustningen innan sådana effekter visar sig.

Tyvärr går det inte plocka ut lika mycket information ang 'wear' som i en SSD-disk och algoritmerna är heller inte lika avancerade som i en SSD. och tyvärr går det heller inte att skicka 'trim' till dom flesta USB/SD-minnen även om man börja fundera på det.

Att tänka på att även om det är en byte som skrivs i tex. en databas, så är det minst en sektor på 512 Byte som skrivs, och sedan är sektorerna samlade i block om 32-128 kB varav allihopa måste flyttas till annat block om blocket skall raderas.

Till detta beror det på minneskontrollern i minnet hur vida den hanterar skrivning till samma sektor upprepande - om det skrivs en ny block var gång eller om denna sektor får sin egen block och kan skriva samtliga sektorer på denna innan det flyttas till nästa lediga block.

idag kosta 8 GB SD under 50 kronor så egentligen skulle man prova att våldskriva till samma sektor med olika slumptal eller uppräkning och läsa tillbaka igen så fort som möjligt och se hur länge det klarar sig innan det blir läsfel - dvs. om det handlar om dagar, veckor, månader eller år och om det bara är en sektor eller om hela minnet börja uppvisa fel samtidigt (dvs. alla block är utslitna)...
Användarvisningsbild
carpelux
Inlägg: 1879
Blev medlem: 13 oktober 2007, 12:33:33
Ort: Varnhem

Re: Hemautomation V2

Inlägg av carpelux »

Jag kör domoticz på en raspberry och har haft problem vid ett par tillfällen med korrupt minneskort. Det har typisk kommit när man satt i eller tagit ur något ur USB-porten.

Jag planerar att NFS-mounta /var och katalogen där domoticz inkl databas huserar för att slippa problemen.
Användarvisningsbild
squiz3r
Inlägg: 5424
Blev medlem: 5 september 2006, 20:06:22
Ort: Lund
Kontakt:

Re: Hemautomation V2

Inlägg av squiz3r »

Har jobbat lite med mjukvaran under tiden som jag väntar på ordern. Nu har jag en fungerande tvåvägs kommunikation mellan dator och obegränsat antal (ja, praktiskt sett i min tillämpning) klienter (dvs smartphone eller surfplatta). Använder mig av sockets i Java. Finns demonstration och förklaring av mjukvaran på youtube.
https://youtu.be/m_gnn8ENSNM
Användarvisningsbild
maDa
Inlägg: 4076
Blev medlem: 11 november 2005, 22:13:16
Ort: Malmö
Kontakt:

Re: Hemautomation V2

Inlägg av maDa »

Du gör alltså utveckligen *på* RPi'n framför TVn? Låter lite omständigt.
Användarvisningsbild
squiz3r
Inlägg: 5424
Blev medlem: 5 september 2006, 20:06:22
Ort: Lund
Kontakt:

Re: Hemautomation V2

Inlägg av squiz3r »

Kopplade bara in den till TV'n nu för att installera operativsystem och testa Java stödet. Väntar på USB WiFI nätverkskort till den samt en touch display. Sen skriver jag ju mjukvaran på min arbetsdator och flyttar över när det är klart. Kommer sätta upp SSH och FTP-server på raspberryn.
Användarvisningsbild
squiz3r
Inlägg: 5424
Blev medlem: 5 september 2006, 20:06:22
Ort: Lund
Kontakt:

Re: Hemautomation V2

Inlägg av squiz3r »

Väntar fortfarande på komponenterna så jag började med GUI till servern. Jag trodde jag hade beställt en 7" skärm men det visade sig att jag tydligen beställde 5" versionen, så det blir ju lite mindre... Blir nog bra ändå. Den skall monteras på väggen bredvid soffan i alla fall, sen kommer jag ju styra allt via telefonen och PIR-sensorer också.

Tar galet mycket tid att designa det i Java så att det rescalar sig bra till olika upplösningar. Så här långt har jag kommit än i alla fall. Väderprognos på vänster sida. Där skall också in temperatur inne och ute från mina givare. Till höger är TV-tablå för gratiskanalerna. I bottnen är knappar för att välja belysningsläge.
printscreen.png
Tror jag kommer ha den med vit text såhär istället. Funderar dock på om röd text kanske är bekvämare när det är mörkt?
printscreen2.png
Väderprognoserna finns som XML utgivna av Yahoo, så de är lätta att ladda ned och parse'a. Skrev bara ihop en enkel klass för att ladda ned ett dokument från webben:

Kod: Markera allt

public class WebDownloader {
    String url;

    public WebDownloader(String url) {
        this.url = url;
    }

    public String getSource() {
        try {
            URL webPageURL = new URL(url);
            URLConnection connection = webPageURL.openConnection();
            BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
            String line;
            StringBuilder source = new StringBuilder();
            while ((line = reader.readLine()) != null)
                source.append(line);
            reader.close();
            return source.toString();
        } catch (MalformedURLException e) {
            System.out.println("Error: Malformed URL for source download");
        } catch (IOException e) {
            System.out.println("Error: IO Exception at source download");
        }
        return "";
    }
}
Sen parse'ar jag det med DOM:

Kod: Markera allt

public class DownloadWeather implements Runnable{
    private static final String WEATHER_URL = "https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20weather.forecast%20where%20woeid%20in%20(select%20woeid%20from%20geo.places(1)%20where%20text%3D%22lund%2C%20skane%2C%20se%22)%20and%20u='c'&format=xml&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys";

    private WeatherMonitor mon = new WeatherMonitor();

    @Override
    public void run() {
        WebDownloader downloader = new WebDownloader(WEATHER_URL);
        while (mon.isUpdating()) {
            String source = downloader.getSource();
            extractWeather(source);
            try {
                Thread.sleep(600000); // 10 minute
            } catch (InterruptedException e) {
            }
        }
    }

    public WeatherMonitor getMonitor(){
        return mon;
    }

    private void extractWeather (String source) {
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            StringBuilder xmlBuilder = new StringBuilder();
            xmlBuilder.append(source);
            ByteArrayInputStream stream = new ByteArrayInputStream(
                    xmlBuilder.toString().getBytes("UTF-8")
            );
            Document document = builder.parse(stream);
            NodeList dayReports = document.getElementsByTagName("yweather:forecast");
            for (int i = 0; i < dayReports.getLength(); i++){
                Node n = dayReports.item(i);
                DateFormat df = new SimpleDateFormat("dd MMM yyyy");
                Date date = df.parse(((Element)n).getAttribute("date"));
                mon.addReport(new WeatherReport(date,
                        Integer.parseInt(((Element) n).getAttribute("high")),
                        Integer.parseInt(((Element)n).getAttribute("low")),
                        ((Element)n).getAttribute("text")
                ));
            }
        } catch (ParserConfigurationException e) {
            System.out.println("Error: Parser configuration");
        } catch (UnsupportedEncodingException e) {
            System.out.println("Error: Unsupported encoding");
        } catch (SAXException e) {
            System.out.println("Error: SAX");
        } catch (IOException e) {
            System.out.println("Error: IO Exception DOM");
        } catch (ParseException e) {
            System.out.println("Error: date parsing");
        }
    }
}
Använder regex för att identifiera fem typer av väder utifrån beskrivningstexten än så länge.

Kod: Markera allt

// Class for storing a single days weather report
public class WeatherReport {
    public Date date;
    public int high, low, type;
    public String text;

    public WeatherReport(Date date, int high, int low, String text) {
        this.date = date;
        this.high = high;
        this.low = low;
        this.text = text;
        setType();
    }

    // Extract the type of weather from the text
    private void setType(){
        if (text.toLowerCase().contains("snow"))
            type = WeatherMonitor.SNOW;
        else if (text.toLowerCase().contains("rain") || text.toLowerCase().contains("showers"))
            type = WeatherMonitor.RAIN;
        else if (text.toLowerCase().contains("partly cloudy"))
            type = WeatherMonitor.PARTLY_CLOUDY;
        else if (text.toLowerCase().contains("cloudy"))
            type = WeatherMonitor.CLOUDY;
        else if (text.toLowerCase().contains("sunny"))
            type = WeatherMonitor.SUNNY;
        else
            type = -1; // Unknown type, use raw text instead of picture
    }

    // Write to string
    public String toString(){
        return date.toString() + ": " +
                high + "'C/" +
                low + "'C " +
                text + ", " +
                type + "\n";
    }

    // Clone
    public WeatherReport clone(){
        return new WeatherReport(date,high,low,text);
    }
}
Sparas i hashmap i en monitor som GUI't kan komma åt.

Kod: Markera allt

package Weather;

import java.util.*;

public class WeatherMonitor {
    private Map<String, WeatherReport> reports = new HashMap<>();
    private boolean update = true;

    // Weather states
    public static final int SNOW = 0;
    public static final int RAIN = 1;
    public static final int PARTLY_CLOUDY = 2;
    public static final int CLOUDY = 3;
    public static final int SUNNY = 4;

    public synchronized void addReport(WeatherReport report){
        reports.put(report.date.toString(), report);
        removeOldReports();
    }

    public synchronized WeatherReport getReportDaysFromNow(int days){
        Date day = new Date();
        day.setTime(day.getTime() + (long)86400000*days);
        String sDate = startOfDay(day).toString();
        if (reports.get(sDate) != null)
            return reports.get(sDate).clone();
        return null;
    }

    public synchronized String toString(){
        String s = "";
        for (WeatherReport report : reports.values()){
            s += report.toString() + "\n";
        }
        return s;
    }

    public synchronized void stop(){
        update = false;
    }

    public synchronized boolean isUpdating(){
        return update;
    }

    private void removeOldReports(){
        Date startOfToday = startOfToday();
        Iterator i = reports.entrySet().iterator();
        while (i.hasNext()){
            if (((Map.Entry<String, WeatherReport>)i.next()).getValue().date.getTime() < startOfToday.getTime())
                i.remove();
        }
    }

    private Date startOfToday(){
        return startOfDay(new Date());
    }

    private Date startOfDay(Date date){
        Calendar startOfDay = Calendar.getInstance();
        startOfDay.setTime(date);
        startOfDay.set(Calendar.HOUR_OF_DAY, 0);
        startOfDay.set(Calendar.MINUTE, 0);
        startOfDay.set(Calendar.SECOND, 0);
        startOfDay.set(Calendar.MILLISECOND, 0);
        return  startOfDay.getTime();
    }
}
TV programmen var lite jobbigare för det verkar inte finnas något API för svenska kanaler. Men det gick snabbt att slänga ihop en enkel crawler som hämtar informationen från tv.nu

Kod: Markera allt

public class TvGuideDownloader implements Runnable{
    private static final String URL = "http://www.tv.nu/";

    ArrayList<String> channels = new ArrayList<>();
    TvGuideMonitor mon = new TvGuideMonitor();

    public TvGuideDownloader(String [] channels) {
        ArrayList<String> ch = new ArrayList<String>(Arrays.asList(channels));
        ch.forEach((c)->this.channels.add(c.toLowerCase()));
    }

    public TvGuideMonitor getMonitor(){
        return mon;
    }

    @Override
    public void run() {
        WebDownloader downloader = new WebDownloader(URL);
        while (true) {
            // Download source from page
            String source = downloader.getSource();
            // Split by channels
            HashMap<String, String> channelData = splitChannels(source);
            // Extract program info
            ArrayList<TvProgram> programs = extractPrograms(channelData);
            // Filter
            programs.removeIf(p -> !channels.contains(p.getChannel().toLowerCase()));
            programs.removeIf(p -> p.getTime().getTime() < (new Date()).getTime());
            // Put in monitor
            mon.setList(programs);
            // Sleep
            try {
                Thread.sleep(60000); // 1 minute
            } catch (InterruptedException e) {
            }
        }
    }

    private HashMap<String,String> splitChannels(String source) {
        String regexp = "zeta color-gentle\\\">";
        String [] s = source.split(regexp);
        HashMap<String,String> channels = new HashMap<>();
        for (int i = 0; i < s.length; i ++)
            if (i != 0){
                String [] channelData = s[i].split("</span>",2);
                channels.put(channelData[0], channelData[1]);
            }
        return channels;
    }

    private ArrayList<TvProgram> extractPrograms(HashMap<String,String> channelData){
        ArrayList<TvProgram> programs = new ArrayList<>();

        String splitPoint = "data-id";
        Pattern rProgramTitle = Pattern.compile("(?<=data-title=\")(.*)(?=\")");
        Pattern rProgramTime = Pattern.compile("(?<=data-start-time-unix=\")(.*)(?=\")");

        for (Map.Entry<String,String> entry : channelData.entrySet()){
            String [] ps = entry.getValue().split(splitPoint);
            for (String data : ps){
                String title = "";
                String time = "";
                Matcher mTitle = rProgramTitle.matcher(data);
                if (mTitle.find())
                    title = replaceHtmlCodes(mTitle.group().split("\"")[0]);
                Matcher mTime = rProgramTime.matcher(data);
                if (mTime.find())
                    time = mTime.group().split("\"")[0];
                if (time.length() > 0 && title.length() > 0)
                    programs.add(new TvProgram(entry.getKey(), title, new Date(Long.parseLong(time) * 1000)));
            }
        }
        return programs;
    }

    private String replaceHtmlCodes(String text){
        String [] symbol = {" ", "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/"};
        for (int i = 32; i <= 47; i ++){
            text = text.replaceAll("\\&#" + Integer.toString(i) + ";", symbol[i-32]);
            text = text.replaceAll("\\&#0" + Integer.toString(i) + ";", symbol[i-32]);
        }
        return text;
    }
}
Du har inte behörighet att öppna de filer som bifogats till detta inlägg.
Användarvisningsbild
squiz3r
Inlägg: 5424
Blev medlem: 5 september 2006, 20:06:22
Ort: Lund
Kontakt:

Re: Hemautomation V2

Inlägg av squiz3r »

Fick min leverans idag. Tog hela kvällen att få igång touchskärmen. Inga instruktioner, tillverkare eller nått. När jag väl lyckades googla fram drivrutiner till den så kraschade datorn varenda gång jag hade installerat dem och jag blev tvungen att installera om en ny avbild av Raspbian för att få igång den. Efter väldigt mycket trixande visade det sig att root partitionen blev full direkt så jag var tvungen att göra partitionen större.

Nu är den igång och kör serverprogrammet i alla fall! Går faktiskt väldigt bra att läsa texten på lite avstånd, trodde att det skulle bli väldans smått på denna skärmen men det känns rätt lagom faktiskt. Notera att jag kör med WiFi dongel trots att den står mindre än 2dm från routern. Man tager vad man haver.
P_20160223_003423.jpg
Du har inte behörighet att öppna de filer som bifogats till detta inlägg.
qx5
Inlägg: 1678
Blev medlem: 14 augusti 2014, 04:23:04

Re: Hemautomation V2

Inlägg av qx5 »

http://tvsajten.com/tv skall ha XML filer för svenska kanaler.

Om det är trögt att skriva i Java så kanske något annat datorspråk för att uttrycka det du vill göra vore bättre?
Användarvisningsbild
squiz3r
Inlägg: 5424
Blev medlem: 5 september 2006, 20:06:22
Ort: Lund
Kontakt:

Re: Hemautomation V2

Inlägg av squiz3r »

Har varit väldigt fullt upp på sista tiden men idag fick jag ett par timmar över. Raspberryn skall styra min belysning med 433Mhz via nexa-mottagarna. Finns ju en hel del dokumentation på internet om protokollet men jag ville veta ID-numret på min sändare ändå så jag bestämde mig för att att göra reverse engineering på min sändare. Sändaren är gjuten/limmad ihop och jag kände inte för att plocka isär den så jag kopplade bara upp oscilloskopet till sändarens batteri.
P_20160312_172131.jpg
Och jodå, mycket riktigt kunde man se att spänningen sjönk när den sänder. Här är vad oscilloskopet visade.
ch2_on.png
Ser inte så givande ut men jag öppnade mätningarna med Matlab och skrev ihop ett skript som först binariserar mätningarna, sen räknar det ut de vanligt förekommande tiderna för hög och låg signal och läser tillsist ut rådatan i sändningen. Här har skriptet plottat den binära datan och pilar som visar vilket håll den har avkodat informationen.
ch2_on.jpg
Utdatan från skriptet blev följande:

Kod: Markera allt

High signal delay: 
278 µS, 
Low signal delay:
300 µS, 
1343 µS, 
---------------100110101010101010010110011010100101101001101010010
010100101101010100110101010101010010110011010100101101001101010010
010100101101010100110101010101010010110011010100101101001101010010
010100101101------------------------------------------------------
Som ni ser så stämmer alla fyra avkodade sändningar överens med varandra, typiskt bra tecken.

Jag tolkar (i motsats till forumets wiki) en 01 som etta och 10 som nolla. Detta gör att min information kan tolkas som följande:

Kod: Markera allt

START  UNIKT ID                                                  Grupp   Av/på   Kanal     Knapp     Stopp
0      1010010110101010011010101010101001011001101010010110      10      01      1010      1001      0
Grupp: 10->0 (Ej gruppkommando)
Av/På: 01-> 1 (på)
Kanal: 1010-> 00
Knapp 1001 -> 01 (knapp 2)
Nu skall det bara hackas ihop ett program till PIC eller Arduino som kan sända och ta emot enligt protokollet. Det skall då såklart kompletteras med dimningsfunktionen.
Du har inte behörighet att öppna de filer som bifogats till detta inlägg.
Användarvisningsbild
squiz3r
Inlägg: 5424
Blev medlem: 5 september 2006, 20:06:22
Ort: Lund
Kontakt:

Re: Hemautomation V2

Inlägg av squiz3r »

Om någon är nyfiken på matlab-skriptet:

Kod: Markera allt

sec_per_div = 25000; % µS
plotOscData_1CH

time_per_sample = sec_per_div * 10 / size(CH1,1);

%% BINARIZE THE SIGNAL
maxVal = max(CH1);
minVal = min(CH1);
midVal = (maxVal + minVal)/2;

n = size(CH1, 1);

CH1_bin = zeros(n,1);
for i = 1:n
    CH1_bin(i) = (CH1(i) > midVal);
end

plot(1:n, CH1_bin);
set(gca,'units','pixels')
set(gcf,'units','pixels')
axesPos = get(gca,'position');
figurePos = get(gcf,'position');
set(gcf,'position',[figurePos(1) figurePos(2) axesPos(3)+50 axesPos(4)+50])
set(gca,'units','normalized','position',[0 0 1 1])


%% FIND ON AND OFF DELAYS
on_delay = [];
off_delay = [];

tRise = 1;
tFall = 1;
lastVal = CH1_bin(1);
for i = 1:n
    if CH1_bin(i) ~= lastVal
        if CH1_bin(i) > 0.5
            off_delay_ = i - tFall;
            off_delay = [off_delay ; off_delay_];
            tRise = i;
        else
            on_delay_ = i - tRise;
            on_delay = [on_delay ; on_delay_];
            tFall = i;
        end
    end
    lastVal = CH1_bin(i);
end

% Make a histogram
on_hist_offset = min(on_delay) - 1;
off_hist_offset = min(off_delay) - 1;
on_hist = zeros(max(on_delay)-on_hist_offset,1);
off_hist = zeros(max(off_delay)-off_hist_offset,1);

on_d_n = size(on_delay,1);
off_d_n = size(off_delay,1);
for i = 1:(max(on_d_n, off_d_n))
    if i <= on_d_n
        on_hist(on_delay(i) - on_hist_offset) = on_hist(on_delay(i) - on_hist_offset) + 1;
    end
    if i <= off_d_n
        off_hist(off_delay(i) - off_hist_offset) = off_hist(off_delay(i) - off_hist_offset) + 1;
    end
end

% Find the common on and off delays
common_on_delays = find(on_hist > on_d_n / 10) + on_hist_offset;
common_off_delays = find(off_hist > off_d_n / 10) + off_hist_offset;

% If two delays are very close, replace with the weighted average
i = 1;
while i < size(common_off_delays,1)
    indexes_close = find(abs(common_off_delays(i)-common_off_delays) < 3);
    if size(indexes_close,1) > 1
        % Calc the product sum of the values and the times they appear
        hist_indexes = common_off_delays(indexes_close)-off_hist_offset;
        common_off_delays(i) = sum(common_off_delays(indexes_close).*off_hist(hist_indexes));
        % Divide by total times they appear
        common_off_delays(i) = common_off_delays(i) / sum(off_hist(hist_indexes));
        % Remove duplicated
        common_off_delays(indexes_close(indexes_close ~= i)) = [];
    end
    i = i + 1;
end
i = 1;
while i < size(common_on_delays,1)
    indexes_close = find(abs(common_on_delays(i)-common_on_delays) < 3);
    if size(indexes_close,1) > 1
        % Calc the product sum of the values and the times they appear
        hist_indexes = common_on_delays(indexes_close)-on_hist_offset;
        common_on_delays(i) = sum(common_on_delays(indexes_close).*on_hist(hist_indexes));
        % Divide by total times they appear
        common_on_delays(i) = common_on_delays(i) / sum(on_hist(hist_indexes));
        % Remove duplicated
        common_on_delays(indexes_close(indexes_close ~= i)) = [];
    end
    i = i + 1;
end

disp('High signal delay: ')
for i = 1:size(common_on_delays,1)
    disp(sprintf('%d µS, ', round(common_on_delays(i) * time_per_sample)))
end
disp('Low signal delay:')
for i = 1:size(common_off_delays,1)
    disp(sprintf('%d µS, ', round(common_off_delays(i) * time_per_sample)))
end

%% DECODE THE SIGNAL

if size(common_on_delays,1) == 1 && size(common_off_delays,1) == 2
    disp('Partial data:');
    data = [];

    % Find the start and end of each transmission
    pause = [;];
    potential_start_of_pause = 1;
    i = 1;
    lastVal = 0;
    for i = 2:n
        if CH1_bin(i) < 0.5 && lastVal == 1
                potential_start_of_pause = i;
        end
        if (CH1_bin(i) > 0.5 && lastVal == 0) || i == n
            if i - potential_start_of_pause > max(common_off_delays) * 5
                pause = [pause [potential_start_of_pause;i]];
            end
        end
        lastVal = CH1_bin(i);
        i = i + 1;
    end
    
    % Start decoding in backward direction
    if (size(pause,2) > 0 && pause(1,1) > 1)
        % Plot direction of parsing arrow
        arrowX = [pause(1,1)/n 0];
        annotation('textarrow', arrowX, [0.5 0.5]);
        % Decode..
        tRise = pause(1,1) + common_off_delays(2); % Always end with zero
        raw_data_back = '';
        lastVal = 0;
        bit_ok = 1;
        i = pause(1,1);
        while size(raw_data_back,2) < 66
            if i > 0
                if CH1_bin(i) ~= lastVal
                    if CH1_bin(i) > 0.5
                        offTime = tRise - i;
                        if bit_ok == 1
                            if find(abs(common_off_delays - offTime) == min(abs(common_off_delays - offTime))) == 1
                                raw_data_back = strcat('1', raw_data_back);
                            else
                                raw_data_back = strcat('0', raw_data_back);
                            end
                        end
                        tFall = i;
                    else
                        onTime = tFall - i;
                        if (abs(onTime - mean(common_on_delays)) < onTime / 2)
                            bit_ok = 1;
                        end
                        tRise = i;
                    end
                end
                lastVal = CH1_bin(i);
            else
                raw_data_back = strcat('-', raw_data_back);
            end
            i = i - 1;
        end
        disp(raw_data_back)
        data = [data ; raw_data_back];
    end
    
    % Start decoding in forward direction
    if size(pause,2) > 0 && pause(2,1)~=n
        for signal = 1:size(pause,2)
            tStart = pause(2,signal);
            % Plot direction of parsing arrow
            if size(pause,2) > signal
                arrowX = [tStart/n pause(1,signal+1)/n];
            else
                arrowX = [tStart/n 1];
            end
            annotation('textarrow', arrowX, [0.5 0.5]);
            % Decode...
            bit_ok = 0;
            raw_data = '';
            lastVal = 0;
            tFall = i;
            tRise = i;
            i = tStart;
            while size(raw_data,2) < 66
                if i <= n
                    if CH1_bin(i) ~= lastVal
                        if CH1_bin(i) > 0.5
                            offTime = i - tFall;
                            if offTime ~= 0
                                if bit_ok == 1
                                    if find(abs(common_off_delays - offTime) == min(abs(common_off_delays - offTime))) == 1
                                        raw_data = strcat(raw_data, '1');
                                    else
                                        raw_data = strcat(raw_data, '0');
                                    end
                                end
                            end
                            tRise = i;
                        else
                            onTime = i - tRise;
                            if (abs(onTime - mean(common_on_delays)) < onTime / 2)
                                bit_ok = 1;
                            end
                            tFall = i;
                        end
                    end
                    lastVal = CH1_bin(i);
                else
                    raw_data = strcat(raw_data, '-');
                end
                i = i + 1;
            end
            disp(raw_data)
            data = [data ; raw_data];
        end
    end
    %% Check if all data is consistent
    ok = 1;
    for i = 1:size(data,2)
        bits = [];
        for j = 1:size(data,1)
            if data(j,i) ~= '-'
                bits = [bits int32(data(j,i)-'0')];
            end
        end
        if range(bits) ~= 0
            ok = 0;
        end
    end
    if ok == 1
        complete_data = '';
        for i = 1:size(data,1)
            complete_data(find(data(i,:) ~= '-')) = data(i,(find(data(i,:) ~= '-')));
        end
        disp('Complete data:');
        disp(complete_data);
        % Parse the data
        if (isempty(find(complete_data == '-')))
            split_on = [1 27 28 29 31 33];
            nbr_spaces = 0;
            parsed_data = [complete_data(1)];
            for i = 2:2:size(complete_data,2)-1
                % Add spaces
                if any(abs(split_on-(size(parsed_data,2)-nbr_spaces))<0.5)
                    parsed_data = [parsed_data '    '];
                    nbr_spaces = nbr_spaces + 4;
                end
                %% Parse a bit
                if complete_data(i) == '0' && complete_data(i+1) == '1'
                    parsed_data = [parsed_data '1'];
                else
                    parsed_data = [parsed_data '0'];
                end
            end
            parsed_data = [parsed_data '    ' complete_data(end)];
            disp('Parsed data:');
            disp(parsed_data);
            parsed_data = strsplit(parsed_data);
            fprintf('ID: %d\nGroup: %d\nValue: %d\nChannel: %d\nButton: %d\n\n', bin2dec(parsed_data(2)), bin2dec(parsed_data(3)), bin2dec(parsed_data(4)), bin2dec(parsed_data(5)), bin2dec(parsed_data(6))+1)
        end
    else
        disp('Data is inconsistent');
    end
else
    displ('To many common on or off times, fine tune parameters to get 1 or 2 on times and 2 off times')
end
Senast redigerad av squiz3r 13 mars 2016, 15:24:46, redigerad totalt 1 gång.
Skriv svar