Meteo stanica 0310 i kako izgleda ispod kućišta [foto] [18+]

Nedavno sam zamijenio svoju skoro 10 godina staru uradi-sam žičnu Ethernet meteo stanicu baziranu na Arduinu (DHT22 + BMP085 + Geiger brojač sa SMB-20 cijevima montiran u drvenu kućicu u dvorištu) s novom integriranom stanicom s Aliexpressa, NicetyMeter 0310 Professional WiFi Weather Station koja, unatoč glupavom nazivu brenda, nije loša za cijenu po kojoj se može nabaviti.

Hardver

Meteo stanica sastoji se od displeja s mogućnošću spajanja na wifi mrežu (napaja se s tri AAA baterije i opcionalnim strujnim adapterom koji je opcionalan samo ako ne mislite koristiti Wifi, ako mislite, bez njega ne radi) i integriranim senzorom koji izgledom dosta podsjeća na Davis Vantage Vue (donje fotke su s web stranice prodavača), ali ima i UV senzor i usput mjeri sunčevo zračenje. Dolazi s kompletom za montažu, a osnovna razlika u odnosu na puno skuplje Davis “nadahnuće” je činjenica da vanjska jedinica ne puni svoje baterije (dakle solarni panel se koristi za napajanje senzora tijekom dana, a tri minjonke ga napajaju preko noći), što znači da se baterije moraju povremeno mijenjati. Proizvođač tvrdi da bi trebale potrajati barem godinu dana, što još trebam provjeriti. Nadopuna – upozorenje za nisku razinu baterije na unutarnjoj konzoli upalilo se nakon 19 mjeseci rada.

Kratkim guglanjem postaje očito da se ova kombinacija displeja i senzora prodaje pod različitim nazivima (Sainlogic WS 0310 i Ragova, između ostalog), dok se vanjski senzor koristi u još više kombinacija prijemnika i trgovačkih marki. Proizvođač je, čini se, StarMeter Instruments.

Cijela stvarčica nudi dobar odnos cijene i kvalitete, a nije šaomi. Kućište je od čvrste ABS plastike, ne odaje dojam da će vam se raspasti u ruci i ne djeluje jeftino. Jedini problem koji sam imao s vanjskim senzorom je priloženi plastični stup za montažu se pomalo klima, pa jači udari vjetra znaju aktivirati klackalicu koja broji količinu oborina. Ovo je možda i moja krivica jer je senzor montiran na antenski stup visine jednog metra koji je, pak, na ljestvama od 7 metara koja su montirana na zadnji vanjski zid kuće.

Skidanje podataka sa stanice

Osim što prikazuje podatke u stvarnom vremenu, displej konzola te podatke šalje na servise  Weather UndergroundWeathercloud. Priloženi priručnik za uporabu čak je i solidno preveden na engleski i jednostavno je konfigurirati stanicu da logira podatke online, ali ja ne bih bio ja da mi je to dosta. Oba servisa imaju svoja ograničenja – Weathercloud prihvaća podatke samo svakih 10 minuta i daje vam pristup samo zadnjoj godini dana vaših podataka bez pretplate, dok Wunderground prihvaća ažuriranja svaku minutu, ali API key koji dobijete za čitanje podataka dozvoljava 1500 poziva dnevno, dakle smijete povući podatke samo jednom u minuti.

Kako mi je proritet bio izraditi sigurnosnu kopiju podataka s moje meteo stanice (i objaviti ih na druge servise) dok ne nađem bolje rješenje, postavio sam genijalno jednostavnu Google Apps skriptu leoherzoga WundergroundStationForwarder koja preuzima podatke s Wundergrounda i prosljeđuje ih drugim servisima. Stvar trenutno radi poprilično pouzdano. Kad sam to riješio, trebalo je zaviriti ispod haube. Senzor šalje podatke svakih 16 sekundi, ali ja imam podatke svaku minutu. Sramota!

Pogled u kućište

Kako je bila velika vjerojatnost da displej ima neku vrstu ESP8266 procesora (Soft AP konfiguracija i njen naziv, WeatherHome-CFxxxxx, bili su glavni razlozi za sumnju), nadao sam se da će taj procesor imati i nekakav serijski debug za čitanje podataka.

Bio sam skoro u pravu. Displej ima ESP-WROOM-02D-V1.4,, ali njegova jedina svrha je logiranje podataka na web servise i sinkronizacija sata preko NTP protokola. Četiri rupice za nožice (označene s P5) pokraj tog procesora su, odozgo prema dolje, VCC, GND, TXD, RXD.

Port UART0 ne daje ništa korisnih podataka za debugging osim uobičajenog boot debuga tipičnog za ESP8266, pa mu je vjerojatna svrha fleširanje procesora. Boot log izgleda ovako:

ets Jan  8 2013,rst cause:1, boot mode:(7,7)

waiting for host
ets Jan  8 2013,rst cause:1, boot mode:(7,7)

waiting for host

ets Jan  8 2013,rst cause:2, boot mode:(3,7)

load 0x40100000, len 2592, room 16 
tail 0
chksum 0xf3
load 0x3ffe8000, len 764, room 8 
tail 4
chksum 0x92
load 0x3ffe82fc, len 676, room 4 
tail 0
chksum 0x22
csum 0x22

2nd boot version : 1.7(5d6f877)
SPI Speed : 40MHz
SPI Mode : QIO
SPI Flash Size & Map: 16Mbit(1024KB+1024KB)
jump to run user1 @ 1000

OS SDK ver: 2.0.0(e271380) compiled @ Mar 30 2018 18:54:06
phy ver: 1055_1, pp ver: 10.7

rf cal sector: 507
tcpip_task_hdl : 40107b28, prio:10,stack:512
idle_task_hdl : 40107bd8,prio:0, stack:384
tim_task_hdl : 40107d20, prio:2,stack:512
Load param success
Timer Server: time.windows.com
20000128128128000001281280
 ets Jan  8 2013,rst cause:2, boot mode:(3,6)

load 0x40100000, len 2592, room 16 
tail 0
chksum 0xf3
load 0x3ffe8000, len 764, room 8 
tail 4
chksum 0x92
load 0x3ffe82fc, len 676, room 4 
tail 0
chksum 0x22
csum 0x22

2nd boot version : 1.7(5d6f877)
SPI Speed : 40MHz
SPI Mode : QIO
SPI Flash Size & Map: 16Mbit(1024KB+1024KB)
jump to run user1 @ 1000

OS SDK ver: 2.0.0(e271380) compiled @ Mar 30 2018 18:54:06
phy ver: 1055_1, pp ver: 10.7

rf cal sector: 507
tcpip_task_hdl : 40107b28, prio:10,stack:512
idle_task_hdl : 40107bd8,prio:0, stack:384
tim_task_hdl : 40107d20, prio:2,stack:512
Load param success
Timer Server: time.windows.com
20000128128128000001281280
Mainboard, bottom view

Matična ploča ima i dva mikroprocesora (chip-on-board, zaliveni u epoksi smolu) koji pogone LCD displej i sve ostalo.

To sve ostalo uključuje:

  • Dvožični I2C EEPROM i senzor tlaka zraka. EEPROM ima izvučene rupe za nožice.
  • Prijemnik 433 MHz s izvučenim nožicama
  • Senzor vlažnosti zraka

Kako je ESP8266 priključen na glavni procesor/procesore preko nožica IO 12, IO 13, IO 14 i IO 15, očito je da komuniciraju preko SPI protokola, ali zaključio sam da bi istraživanje kako komuniciraju uzelo previše vremena, a procesor se javlja ESP modulu samo jednom u minuti kada treba logirati podatke. Podatke s vanjskog senzora morat ću pribaviti na neki drugi način.

Prisluškivanje komunikacije vanjskog senzora

Sljedeći je korak bio priključiti SDR (software-defined radio) prijemnik baziran na čipu RTL2832U (verzija 25 MHz do 1760 MHz, jeftikaner s Alija za 10 dolara) i vidjeti što on čuje na 433 MHz pomoću dekodera RTL_433. Nije razočarao. Najnovija verzija dekodera RTL_433 prepoznala ga je kao Cotech-367959 (i druge stanice koje koriste isti senzor, kao što su SwitchDoc Labs Weather FT020T,  Sainlogic Weather Station WS019T i vjerojatno mnoge druge).

RTL_433 identifies the station as Cotech-36759

Imajte na umu da trenutno samo najnovija verzija RTL-433 podržava ovaj senzor u potpunosti i pravilno dekodira UV indeks i intenzitet sunčevog zračenja, dok Debian paket (ako ga instalirate s apt install rtl-433) nema to dvoje, pa sam ga morao buildati iz sourcea za moj Orange Pi Zero na Armbianu.

Korištenje podataka

Ovo je pak značilo da podatke stanice jednostavno mogu uvesti u WeeWX pomoću weewx-sdr drajvera. Međutim, taj drajver trenutno ne podržava senzor Cotech-367959, pa je trebal dodati tu klasu u datoteku sdr.py. Ona se obično nalazi na weewx/user/sdr.py nakon instalacije ekstenzije u weewx.

class Cotech367959Packet(Packet):
    # ['{"time" : "2022-04-28 14:25:59", "model" : "Cotech-367959", "id" : 109, "battery_ok" : 1, "temperature_F" : 63.000, "humidity" : 55, "rain_mm" : 16.200, "wind_dir_deg" : 296, "wind_avg_m_s" : 0.900, "wind_max_m_s" : 1.300, "light_lux" : 23093, "uv" : 14, "mic" : "CRC"}']
    IDENTIFIER = "Cotech-367959"

    @staticmethod
    def parse_json(obj):
        pkt = dict()
        pkt['dateTime'] = Packet.parse_time(obj.get('time'))
        pkt['usUnits'] = weewx.METRIC
        sensor_id = obj.get('id') # changes every time you reset the outdoor sensor
        pkt['battery'] = 0 if obj.get('battery') == 'OK' else 1
        pkt['temperature'] = to_C(Packet.get_float(obj, 'temperature_F'))
        pkt['humidity'] = Packet.get_float(obj, 'humidity')
        pkt['wind_gust'] = Packet.get_float(obj, 'wind_max_m_s')
        pkt['wind_speed'] = Packet.get_float(obj, 'wind_avg_m_s')
        pkt['wind_dir'] = Packet.get_float(obj, 'wind_dir_deg')
        pkt['total_rain'] = Packet.get_float(obj, 'rain_mm')
        pkt['light_lux'] = Packet.get_int(obj, 'light_lux')
        pkt['uv'] = Packet.get_int(obj, 'uv') / 10
        pkt = Packet.add_identifiers(pkt, sensor_id, Cotech367959Packet.__name__)
        return pkt

Imajte na umu i da se vrijednost id mijenja kad god pritisnete tipku za resetiranje na vanjskom senzoru. Ako ste identificirali svoj senzor i namjeravate ga trajno koristiti na ovaj način, preporučam da tu vrijednost zamijenite fiksnom.

Kao što smo uočili u komentarima članka, izvorni sdr.py nema funkciju koja radi konverziju stupnjeva Fahrenheita u Celsiusove, pa je trebate dodati (pomoćne funkcije počinju od 169. retka sdr.py).

def to_C(v):
    if v is not None:
        v  = 5 / 9 * (v - 32)
    return v

Da biste vidjeli te podatke u WeeWXu, morate mapirati mjerenja u datoteci /etc/weewx/weewx.conf:

[SDR]
    # This section is for the software-defined radio driver.
    # The driver to use
    driver = user.sdr

    [[sensor_map]]
        windDir = wind_dir.109.Cotech367959Packet
        windSpeed = wind_speed.109.Cotech367959Packet
        windGust = wind_gust.109.Cotech367959Packet
        outTemp = temperature.109.Cotech367959Packet
        outHumidity = humidity.109.Cotech367959Packet
        rain_total = total_rain.109.Cotech367959Packet
        UV = uv.109.Cotech367959Packet
        luminosity = light_lux.109.Cotech367959Packet

U gornjem primjeru 109 je ID moje stanice, vaš će se razlikovati (ako ga ne zamijenite fiksnim, što preporučam, vidi gore).

Sada sam imao gotovo sve podatke koje sam trebao, osim tlaka zraka – vanjska jedinica nema taj senzor. U tu svrhu na svoj Orange Pi / Raspberry Pi sam spojio BME180 breakout pločicu preko I2C porta. Podatke sam povukao u WeeWX ovim Bme280wx drajverom.

Trebalo je kombinirati podatke – vrijednosti senzora BME180 mapirao sam kao inHumidity, inTemp i pressure u datoteci /etc/weewx/weewx.conf kao u donjem primjeru.

[Bme280wx]
i2c_port = 0 # OrangePi and older Raspberry Pi's use 0, usually it's 1
i2c_address = 0x76
usUnits = US
temperatureKeys = inTemp
temperature_must_have = ""
pressureKeys = pressure
pressure_must_have = outTemp
humidityKeys = inHumidity
humidity_must_have = ""

Uz to, treba reći WeeWXu da učita taj dodatni drajver u odjeljku Services datoteke  weewx.conf.

[Engine]

    # The following section specifies which services should be run and in what order.
    [[Services]]
        data_services = "", user.bme280wx.Bme280wx

Vrijednost station_type u odjeljku [Station] mora biti navedena kao SDR, jer je to glavni drajver, a BME280 je dodatni.

[Station]
    [...]

    # Set to type of station hardware. There must be a corresponding stanza
    # in this file, which includes a value for the 'driver' option.
    station_type = SDR

Weewx s tim podatcima dostupan je na
https://meteo-valpovo.žnj.hr/

Budućnost

Kako ne polažem previše nade da će jeftini SDR prijemnik savršeno raditi godinama, možda ću pokušati složiti dedicirani prijemnik 433 MHz baziran na ESP32 ili ESP8266 procesoru koji će prosljeđivati podatke dalje. Za ovo mi nedostaje port RTL-433 dekodera u obliku biblioteke za Arduino, pa mi preostaje čekati da netko pametniji to odradi.

Za sada mi je dovoljno imati lokalnu kopiju podataka sa svoje meteo stanice i drugi način objavljivanja podataka na vanjskim servisima koji ne ovisi o samom prijemniku koji sam dobio sa stanicom jer sumnjam da će izaći ažuriranje firmvera u slučaju da Weather Underground ili WeatherCloud iznenada promijene svoje API-je za slanje podataka.

15 misli o “Meteo stanica 0310 i kako izgleda ispod kućišta [foto] [18+]”

  1. Hi. I am using same weather station. Can’t get uv to work. Can you share your 433 config file? Also what repo etc you used to compile? Thanks in advance

    1. RTL_433 was compiled using the official repo, https://github.com/merbanan/rtl_433. However, you might want to hold off for a bit, UV is incorrectly reported without a decimal point, while solar radiation value (lux) is capped. It’s being reworked as we speak, see here.
      What are you running weewx on?

  2. Thanks for that. I am using Home Assistant with an add in that has rtl_433 in a container – Docker? – exposing sensors via MQTT. That’s an interesting debate on Github and looks like the code will be updated one way or another.

    1. Docker tends to complicate matters in this case, although I understand that it’s the easiest way to set up RTL_433 if you’re using homeassistant. If the RTL_433 docker container is built from the latest github sources or the last release version (21.12), both UV (scaled by 10) and light_lux (capped at 69627) should be there, there’s no need to map any values.

  3. Hi. Tried with a similar station, getting an error on weewx startup:
    CRITICAL main: Caught unrecoverable exception:
    Jul 16 13:36:27 packetpi weewx[2527] CRITICAL main: **** name ‘to_C’ is not defined
    Jul 16 13:36:27 packetpi weewx[2527] CRITICAL main: **** Traceback (most recent call last):
    Jul 16 13:36:27 packetpi weewx[2527] CRITICAL main: **** File “/usr/share/weewx/weewxd”, line 153, in main
    Jul 16 13:36:27 packetpi weewx[2527] CRITICAL main: **** engine.run()
    Jul 16 13:36:27 packetpi weewx[2527] CRITICAL main: **** File “/usr/share/weewx/weewx/engine.py”, line 208, in run
    Jul 16 13:36:27 packetpi weewx[2527] CRITICAL main: **** for packet in self.console.genLoopPackets():
    Jul 16 13:36:27 packetpi weewx[2527] CRITICAL main: **** File “/usr/share/weewx/user/sdr.py”, line 3320, in genLoopPackets
    Jul 16 13:36:27 packetpi weewx[2527] CRITICAL main: **** for packet in PacketFactory.create(lines):
    Jul 16 13:36:27 packetpi weewx[2527] CRITICAL main: **** File “/usr/share/weewx/user/sdr.py”, line 3175, in create
    Jul 16 13:36:27 packetpi weewx[2527] CRITICAL main: **** pkt = PacketFactory.parse_json(lines)
    Jul 16 13:36:27 packetpi weewx[2527] CRITICAL main: **** File “/usr/share/weewx/user/sdr.py”, line 3191, in parse_json
    Jul 16 13:36:27 packetpi weewx[2527] CRITICAL main: **** return parser.parse_json(obj)
    Jul 16 13:36:27 packetpi weewx[2527] CRITICAL main: **** File “/usr/share/weewx/user/sdr.py”, line 1187, in parse_json
    Jul 16 13:36:27 packetpi weewx[2527] CRITICAL main: **** pkt[‘temperature’] = to_C(Packet.get_float(obj, ‘temperature_F’))
    Jul 16 13:36:27 packetpi weewx[2527] CRITICAL main: **** NameError: name ‘to_C’ is not defined
    Jul 16 13:36:27 packetpi weewx[2527] CRITICAL main: **** Exiting.

    Any ideia why?

    1. Yeah, you’re missing the to_C function which converts rtl_433’s and the weather station’s (native) Fahrenheit temperature value to degrees Celsius.
      You can do one of two things:
      1) add the function among the utility functions at the top of sdr.py, for example:

      def to_F(v):
          if v is not None:
              v  = v * 1.8 + 32
          return v
      
      def to_C(v):
          if v is not None:
              v  = 5 / 9 * (v - 32)
          return v

      or 2) Omit the conversion in sdr.py and let rtl_433 do that F to C conversion for you. To do this, change
      pkt['temperature'] = to_C(Packet.get_float(obj, 'temperature_F'))
      to
      pkt['temperature'] = Packet.get_float(obj, 'temperature_F')
      and change DEFAULT_CMD in sdr.py from
      DEFAULT_CMD = '/usr/local/bin/rtl_433 -M utc -F json'
      to
      DEFAULT_CMD = '/usr/local/bin/rtl_433 -M utc -F json -C si'

      Of course, there is no need to do both 🙂

      1. Thank you, Ivan. First method worked fine. Secondo also worked, but was missing outside temperature, so changed back to first.
        Hope for some further developments on the driver, there are a lot of these stations out there.

        Paulo

        1. Truth be told, I haven’t tested out the second method out so it’s purely theoretical. I might have missed something. Thanks for getting in touch, I’ve amended the original post to include the missing function.
          I should contact the author of the driver with this issue, but first we have to resolve the rtl_433 lux value issue before we proceed.

  4. Thanks for writing all this up, I’m going to give it a whirl as well for the 0310 I just ordered. I thought I’d be able to use this ecowitt integration in home assistant to connect it but realize I overjudged what was possible. I wish they would have just given a option to direct the data to a different source instead in the firmware but what can you do.

  5. Hello, I want to ask whether it is possible to calibrate the barometer of this weather station? Thanks.

    1. Not as far as I know, the only barometer-related setting mentioned in the console user manual is switching between displaying relative and absolute pressure (and different units).

  6. Could someone tell me how to calculate this in celsius based on this

    // Get the temperature in degrees F
    float get_temperature(const uint8_t *msg) {
    return float((msg[9] & 0b111) * 256 + msg[10]) / 10 – 40;
    }

    1. Where is this snippet from and where do you need the degrees Celsius?
      The general formula for F to C is to subtract 32 and divide the result by 1.8. So I guess something like the following, although I’m sure it could be done more elegantly.

      float get_temperature(const uint8_t *msg) {
      return ((float((msg[9] & 0b111) * 256 + msg[10]) / 10 – 40) - 32)/1.8 ;
      }

  7. Thinking you missed the obvious.

    The device is a pure weather information collector WITH and added WiFi interface based on esp8266. While it may seem like nothing, what you want to do is replace the firmware that only sends to WeatherUnderground and Weathercloud, and send it to Home Assistant.

    Challange is finding out how the WROOM is collecting the data from the device before shipping off to the web. Infact, if one can just redirect the web interface a little with a new firmware, that would do the trick.

    Why try to build an 8266 433 receiver when almost everything is already there for the using?

    1. Like I said, that was my original intention, but reverse-engineering the SPI communication between the WROOM and the undocumented mainboard is too much work just to get updates every 60 seconds. I’m not sure if the mainboard initiates the update or WROOM polls the data from the mainboard, but the sensor unit sends data packets every 16 seconds, thus providing more data. I couldn’t find any firmware sources for the WROOM and, since I have only unit, any reverse-engineering would mean the whole system would be out of commission while I tinker with it.
      Additionally, using/modifying the console that came with the sensor just gives me a single point of failure. As it happens, my RTL433 setup has been very stable. No missing updates. There was an issue this May which could have been prevented if I had remembered to setup a watchdog on the board (for some reason the board froze and it took me a long while to realise that). The original console, meanwhile, continued logging the data to wunderground so I can import the missing data into Weewx from there. I prefer having a redundancy.

Odgovori na ivanOtkaži odgovor

Ova web-stranica koristi Akismet za zaštitu protiv spama. Saznajte kako se obrađuju podaci komentara.