opendata-stuttgart / sensors-software

sourcecode for reading sensor data
571 stars 308 forks source link

Visualize current values via RGB LED #149

Open no-username42 opened 6 years ago

no-username42 commented 6 years ago

I think the relation between sensor and user could be enhanced significantly if the sensor provides actual live information without referring to the map website.

This could be achived via RGB LED(s) that indicates the current polution as color code similar to the website. This would be much cheaper and easier to implement than an LCD display and probably even more useful. With a quick glance at the sensor one can see if the LEDs are green, yellow or red - or anything inbetween. One LED would be enough to show the highest of both values, two of them could indicate the 2.5 and 10 values.

In comparison, an LCD display is more of a hassle: one must be near anough to actually read it, probably wat until the needed screen is shown and than interpret the numeric value as low, medium or high. There is no such mental efford with a more or less red glowing (or even blinking) warning LED.

Using RGB LEDs can be used for even more: we have the unused blue color, that can indicate wlan connection errors (blinking) or strength (ranging from blue to purple) for example.

A RGB LED costs about 1€, the needed resistor even less. So for max 3€ the interaction with the user and everyday benefit from runing a sensor would be largely improved.

Is an noteworthy amount of coding required for this feature? (I´m not a coder.)

ricki-z commented 6 years ago

For a RGB LED we need 3 free analog pins. But we don't have any free pins left. It may be possible to connect an I2C RGB LED, but the costs are about 8€, thats more than a LCD display.

no-username42 commented 6 years ago

Ok - no free pins is an issue. In Hamburg we use (mostly?) the 30 pin board, so lots of pins available. Didn´t know the display was so cheap. Would still prefer the LEDs for a quick check. Any chance that anyone implements this?

ricki-z commented 6 years ago

We also use the 30 pin board. Please look at the pinout of the NodeMCU. Most pins on the 'left' side are blocked by power and the SPI interface. The SPI interface is used to connect the internal flash memory. Two pins on the 'right' side are reserved for the serial/usb interface.

amandel commented 6 years ago

For the LED check out APA106 LED or similar. You can control even multiple with one pin. Could even use a WS2812 RGB LED stripe as even more visible metering display.

Adorfer commented 6 years ago

a) there are single RGB-LEDs and even LED-Stripes (individual adressable) running on I2C. So it could go on the same SCL/SDA as the BME/BMP (no additional ESP pins needed)

b) split between "sensors ESP" and "display ESP", letting them communicate as pair directly in the local wifi (location between via protocol to define). Installing the display inside, then sensor outside. A matrix 8x32 pixels would be interesting, https://www.sparkfun.com/products/13304 to run.

dokape commented 6 years ago

To b): The protocol is no problem. The slave-ESP should get all data by calling the http://"MasterESP-IP"/data.json Try yourself in the browser.

Naesstrom commented 6 years ago

Was going to make a suggestion about this to, simplest way would be to use a addressable like ws2812 or even a neopixel ring. Using that kind it only would need a single Digital pin.

Sure the cost of some of them might be a bit high but then again this isn't necessary but more a nice feature to have.

moonberry-pl commented 5 years ago

There is no problem with connecting the ws2812 (neo) leds, I did it. But we have to show the average values from the last hour and assign colors to the aqi ranges. I'm working on it :)

Gelaener commented 5 years ago

Hi Moonberry, I am also connecting three ws2812 neopixels (showing PM1.0, PM2.5 and PM10). I am using a Wemos D1-mini with PMS7003 sensor. I adjusted the original airRohr-firmware.ino such as include the Adafruit NeoPixel library and neopixel commands. But I got stuck on compilation errors with Arduino IDE. Can you help me?

ricki-z commented 5 years ago

We could help if you send us the compilation errors here ;-)

JanBosNL commented 4 years ago

I see a solution for the lack of pins. I was thinking about a secondary nodeMCU connected to some RGB leds that gets data values from from the luftdaten sensor in your network. (YOUR NODEMCU IP/data.json) and visualises the data in airquality colours like the luftdaten map does.

Also There is a project called stofananas from a dutch hackerspace https://revspace.nl/StofAnanas that looks like it already has a lamp working on json data aquired from JSON data collected by the luftdaten project?

stealth-ultd commented 4 years ago

Just checked the price of WS2812B led shields directly from China, bit over 0,10 € per piece. Would be a nice feature to add.

@moonberry-pl: did you eventually implement the code, could you share it?

JanBosNL commented 4 years ago

Is there code here somewhere I can try out? @moonberry-pl ?

So this would be another secondary NodeMCU getting data from the Luftdaten NodeMCU on my internal network. Using these measurements to control colour and intesity of RGB leds through the fastled Lybrary. In this case an APA102 ledstrip.

I have tried writing up some myself, but cant realy get the JSON data read propperly. Below you can find some of my NOT working code I am struggeling with. Maybe someone here can help out.

Ps. I am a noob but at least I tried! ;)

#include <FastLED.h>

#define FASTLED_ESP8266_NODEMCU_PIN_ORDER

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ArduinoJson.h>

#define DATA_PIN   D8 
#define CLOCK_PIN   D7
#define COLOR_ORDER BGR
#define CHIPSET     APA102
#define NUM_LEDS    8

CRGB leds[NUM_LEDS];

#define BRIGHTNESS  50

// WiFi Parameters
const char* ssid = "YOURSSID";
const char* password = "YOURPASSW";

void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid, password);

  FastLED.addLeds<APA102, DATA_PIN, CLOCK_PIN, BGR>(leds, NUM_LEDS);
  FastLED.setBrightness( BRIGHTNESS );

  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting...");
  }
}

void loop() {

  // Check WiFi Status
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;  //Object of class HTTPClient
    http.begin("http://192.168.178.143/data.json"); //Your internal network IP that the Luftdaten 
                                                                            //Sensor is at. The Json data you find there can be 
                                                                            //copied to -> https://arduinojson.org/v6/assistant/ 
                                                                            //to get parsed code etc...
    int httpCode = http.GET();
    //Check the returning code                                                                  
    if (httpCode > 0) {
      // Get the request response payload
      String payload = http.getString();

      // TODO: Parsing ???????

const size_t capacity = JSON_ARRAY_SIZE(10) + 10*JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(3) + 440;
DynamicJsonDocument doc(capacity);

///*

const char* json = "{\"software_version\":\"NRZ-2019-128-B2\",\"age\":\"138\",\"sensordatavalues\":[{\"value_type\":\"SDS_P1\",\"value\":\"47.65\"},{\"value_type\":\"SDS_P2\",\"value\":\"23.75\"},{\"value_type\":\"temperature\",\"value\":\"3.70\"},{\"value_type\":\"humidity\",\"value\":\"98.60\"},{\"value_type\":\"BMP280_pressure\",\"value\":\"101622.91\"},{\"value_type\":\"BMP280_temperature\",\"value\":\"3.92\"},{\"value_type\":\"samples\",\"value\":\"4389606\"},{\"value_type\":\"min_micro\",\"value\":\"32\"},{\"value_type\":\"max_micro\",\"value\":\"20146\"},{\"value_type\":\"signal\",\"value\":\"-76\"}]}";

deserializeJson(doc, json);

const char* software_version = doc["software_version"]; // "NRZ-2019-128-B2"
const char* age = doc["age"]; // "138"

JsonArray sensordatavalues = doc["sensordatavalues"];

const char* sensordatavalues_0_value_type = sensordatavalues[0]["value_type"]; // "SDS_P1"
const char* sensordatavalues_0_value = sensordatavalues[0]["value"]; // "47.65"

const char* sensordatavalues_1_value_type = sensordatavalues[1]["value_type"]; // "SDS_P2"
const char* sensordatavalues_1_value = sensordatavalues[1]["value"]; // "23.75"

const char* sensordatavalues_2_value_type = sensordatavalues[2]["value_type"]; // "temperature"
const char* sensordatavalues_2_value = sensordatavalues[2]["value"]; // "3.70"

const char* sensordatavalues_3_value_type = sensordatavalues[3]["value_type"]; // "humidity"
const char* sensordatavalues_3_value = sensordatavalues[3]["value"]; // "98.60"

const char* sensordatavalues_4_value_type = sensordatavalues[4]["value_type"]; // "BMP280_pressure"
const char* sensordatavalues_4_value = sensordatavalues[4]["value"]; // "101622.91"

const char* sensordatavalues_5_value_type = sensordatavalues[5]["value_type"]; // "BMP280_temperature"
const char* sensordatavalues_5_value = sensordatavalues[5]["value"]; // "3.92"

const char* sensordatavalues_6_value_type = sensordatavalues[6]["value_type"]; // "samples"
const char* sensordatavalues_6_value = sensordatavalues[6]["value"]; // "4389606"

const char* sensordatavalues_7_value_type = sensordatavalues[7]["value_type"]; // "min_micro"
const char* sensordatavalues_7_value = sensordatavalues[7]["value"]; // "32"

const char* sensordatavalues_8_value_type = sensordatavalues[8]["value_type"]; // "max_micro"
const char* sensordatavalues_8_value = sensordatavalues[8]["value"]; // "20146"

const char* sensordatavalues_9_value_type = sensordatavalues[9]["value_type"]; // "signal"
const char* sensordatavalues_9_value = sensordatavalues[9]["value"]; // "-76"
    //*/

int integervalue = (sensordatavalues[0]["value"]);
Serial.print (integervalue);

// The fastled part needs reviewiewing and better implementation.

/* if (sensordatavalues_0_value > 0 < 15);
        int stofvalueblue = (sensordatavalues_0_value);

            for(int stofvalueblue = 0; stofvalueblue < NUM_LEDS; stofvalueblue++) { 
            leds[stofvalueblue] = CRGB::Blue;
            FastLED.show();
            // clear this led for the next time around the loop
            leds[stofvalueblue] = CRGB::Black;
            delay(30);
        }
if (sensordatavalues_0_value > 15 < 25);
        int stofvaluegreen = (sensordatavalues_0_value);

            for(int stofvaluegreen = 0; stofvaluegreen < NUM_LEDS; stofvaluegreen++) { 
            leds[stofvaluegreen] = CRGB::Green;
            FastLED.show();
            // clear this led for the next time around the loop
            leds[stofvaluegreen] = CRGB::Black;
            delay(30);
        }
        if (sensordatavalues_0_value > 25 < 40);
        int stofvalueyellow = (sensordatavalues_0_value);

            for(int stofvalueyellow = 0; stofvalueyellow < NUM_LEDS; stofvalueyellow++) { 
            leds[stofvalueyellow] = CRGB::Yellow;
            FastLED.show();
            // clear this led for the next time around the loop
            leds[stofvalueyellow] = CRGB::Black;
            delay(30);
        }
if (sensordatavalues_0_value > 40 < 60);
        int stofvalueorange = (sensordatavalues_0_value);

            for(int stofvalueorange = 0; stofvalueorange < NUM_LEDS; stofvalueorange++) { 
            leds[stofvalueorange] = CRGB::Orange;
            FastLED.show();
            // clear this led for the next time around the loop
            leds[stofvalueorange] = CRGB::Black;
            delay(30);
      } */ 

      // Delay
  delay(6000);
}
  }
}
RikDrabs commented 4 years ago

@JanBosNL Groeten uit België! We zijn hier bij luchtpijp.be ook bezig met de fijnstofmeters van luftdaten, en ik heb voor het project een display gemaakt die lokale waarden toont, bv. van een buiten- en binnen-fijnstofmeter. Ik heb ook die horde moeten nemen om het JSON bericht te decoderen. Ik heb daarvoor geen Json library gebruikt. Ben je geïnteresseerd in dat apart stuk, of in mijn heel display project? (+ 1600 lijnen source) Mijn display gebruikt 16 stuks 8x8 dot matrix displays, in blokken van 4 gecascadeerd, en gestuurd door een MCU. Kostprijs hardware +/- 20€ + 8€ voor een afwerk aluminium profiel. (zie foto's) Photos.zip Een andere mogelijkheid is dat ik mijn source aanpas, zodat zij ook RGB LEDS kan aansturen, zoals jij nodig hebt. Lijkt mij een interessante uitbreiding !

bertrik commented 4 years ago

Hi, I designed the stofananas/stoflama from https://revspace.nl/StofAnanas I was inspired from stoflamp.nl, but made it much simpler w.r.t. hardware (no dedicated PM sensor needed, no dedicated RF link between the PM sensor and the indicator), just using WiFi. It's almost fully autoconfigurating, just enter the WiFi credentials (like you do for the luftdaten sensor), then it will automatically find its geographical location (from surrounding APs and the Mozilla database) and then automatically find the nearest luftdaten PM sensor too. I get a lot of positive feedback on it, people like the immediate visual indication, very simple to use. Just one look and you get an impression of the current particulate matter concentration. You can use it even if you don't have a particulate matter sensor yourself.

It's quite inexpensive to build, the ESP8266 is a wemos d1 mini board of about E2,- the LED shield is less than E2,- and both will fit in a lot of E2-, white semi-translucent plastic lamps (many of the lamps appear to use the same size led module). The most expensive part is probably the USB power supply + cable :)

JanBosNL commented 4 years ago

Hallo @RikDrabs en @bertrik,

Tof dat jullie reageren. Nice you guys are here.

I would love to have a look at your code @RikDrabs please share it in a repository here if you dont mind. 1600 pages sounds like a lot, but I like your Matrix sign.

The Stofanas code from @bertrik I have tried to compile through the arduino IDE, but I cant get it to work. Its missing some lirarys and I cant seem to find them anywhere. I tried compiling https://github.com/bertrik/stofananas/blob/master/stofananas.ino throug the arduino IDE but get errors bcause of:

#include "cmdproc.h"

#include "editline.h"

these 2 librarys,

The error message I receive is

'cmd_t' does not name a type

Both projects are very interesting and something in the direction I would like to achieve. Thing is we want to place an artwork in the shape of a set of lungs in a meeting centre in citycentre Arnhem. Arnhem has a new Biomass Centrale and there is a lot of controverse about this.

So I am helping https://www.arnhemspeil.nl/acties/luchtdata-project.html roll out 200+ sensors in and around Arnhem to get the data needed to get this Biomassa Centrale on the political agenda. Or at least be able to verify the claimed (no fijnstof health hazzard data found by the city council) by civilians.

Anyway, This meeting centre in citycentre Arnhem is next to the Gemeentehuis Arnhem, and they want a sign or this RGB set of lungs to make a statement and get attention for the poor air quality in Arnhem.

In my idear perfection would be simple code reading JSON data either directly from a luftdaten sensor module in the home network, Even better of course is the road Stofananas took by getting Json Data from the luftdaten databas.

Thing is there is no internet connection meeting centre in citycentre Arnhem. That is why I was hoping to circumvent the need for a webconnection by setting up a small internal network through which te 2 nodeMCU's can communicate and can be reached by theid IP adress. SO a Luftdaten sensor outdoors, Connected to a home network/router not connected to the internet. Then the RGB or visualiser NodeMCU indoors within connected to this same network getting data from the outdoors Luftdaten NodeMCU.

Untill now I was not able to compile the stofananas through the Arduino IDE, My own code was a try, but the learning curve to get it running has been steep and I have not managed to get the Jsondata to update its values and getting these values to command fastled routines.

Seems simple, but I cant get it running.

I visualised the code and functions like this.

get the JSON data from either an internal Ip adres (like 192.168.178.122/json.data on the nodemcu connected in your network. Or from a webadres when connected to wifi.

2 Get the value from the PMsensor and visualise the value in colours updating everytime the Json data imported from the luftdaten module changes,

This code then has prewritten paths to different Led types and esp8266 boards,

and has an easy way to manipulate value treshold at which certain colours are called.

Thats all...

Great job both of you for inspiring me to try myself, and I would love to get some help getting this running. And in a way, the code from the stofananas seems to be completed and ready for my job great work @bertrik , I just cant get it compiled. And I dont own a d1mini and d1mini rgb shield. So Maybe the best way would be to see why It wont compile through the arduino IDE and once that works, altering some the code so we can choose to use different led types and counts would be enough.

bertrik commented 4 years ago

@JanBosNL To compile the stofananas source code, you need all of the files in the github archive (also the .cpp and .h files), not just the .ino file. I built it using the platformio build system, this is easiest if you are on Linux. It should work too if you use the Arduino IDE and manually install the libraries mentioned in the platformio.ini file: FastLED, WiFiManager and ArduinoJSON (v6). You can contact me privately on bertrik@gmail.com with your address so I can send you the Wemos D1 mini + LED shield hardware.

Another interesting possibility is to control a full size 230V RGB LED lamp through some kind of home automation setup. This might be preferable over a ESP8266 solution if you need more light than a LED shield can provide, also uses standard light sockets. I have not fully worked this out yet.

JanBosNL commented 4 years ago

@bertrik I tried to compile your stofananas now with all the files in the INO directory. Again I get stuck. This time i get the error

no matching function for call to 'HTTPClient::begin(WiFiClient&, String&)'

I tried updating all my librarys and have googled what the problem might be, but cant find a solution yet.

Then I installed platformIO to see if I am able to compile through that IDE, but I am completely lost and not familiar with this platform, so that didn't work out yet..

Your idear of using of the shelve smart ligt bulbs might work. see https://hack42.nl/wiki/LSC_Smart_Connect_Flitsfeestje seems some of the action stuff has esp8266 internals. In this list the smart rgb ledstrip https://www.action.com/nl-nl/p/lsc-smart-connect-slimme-multicolor-ledstrip/ might do the trick, If there is a way to flash your own firmwhere easely.

Thanks for the offer to send me the Wemos d1 mini and shield, at this moment though I would like to keep trying to find another solution. Where I can get more leds fired up. The lungs need a bit more light I think.

For now I am putting this sideproject on pause.

unsermanninchina commented 3 years ago

After I found this information here I finally build a Wemos D1 mini with a RGB-LED shield. I used the ArduinoIDE to install the software. It works great. But my RGB shield works not with the same values as colors representations. I found the values by try and error and than changed the code. 0 is green, 196 is red for example. Thank you for the hint on your webpage, @bertrik !

I would like to get the PM json data from my two local airrohrs. I found the data at http://airrohr-IP/data.json.

The local json raw data is: {"software_version": "NRZ-2020-131", "age":"28", "sensordatavalues":[{"value_type":"PMS_P0","value":"125.50"},{"value_type":"PMS_P1","value":"190.00"},{"value_type":"PMS_P2","value":"134.50"}

Because of this I changed the code at all places with "P1" to "PMS_P1" after changing and I changed "https://data.sensor.community/airrohr/v1/sensor/" + luftdatenid + "/" into "http://airrohr_IP/data.json".

I have no knowledge about the programming language and the use of the local json data didn't work out. I understand there is much more code I wouldn't need in this case, like the automatic search of the nearest station.

Anyway. It works through the online data and I want to say thank you to @bertrik here!