SuperHouse #40: DIY air quality sensor part 3 - software

Sdílet
Vložit
  • čas přidán 25. 08. 2024
  • * www.superhouse...
    * www.superhouse...
    It's surprisingly easy to make your own simple air quality sensor. All you need is a cheap laser-scattering particulate matter sensor, a Wemos D1 Mini, a soldering iron, and Tasmota.
    Part 1 showed how to make the simplest possible air quality sensor. Part 2 extended the project to add a 128x32 pixel OLED display and a mode button, and to install custom firmware to make the sensor last longer. Part 3 is a detailed walkthrough of the source code.
    See the link above for a step by step guide.
    SuperHouse:
    - www.superhouse.tv
    - / superhousetv
    - / superhousetv
    Jonathan Oxer:
    - / jonoxer
    - / jonoxer
    Support me on Patreon to receive exclusive discount codes, access to Patron-only sections of the SuperHouse Discord, and early access to new episodes: / superhouse

Komentáře • 63

  • @SuperHouseTV
    @SuperHouseTV  Před 4 lety +2

    Oops, I mis-spoke when I was complaining about Hungarian notation: technically C isn't a "strongly typed" language, but I think my basic point still stands and the type system will catch many problems related to variable mis-assignment.

    • @flymypg
      @flymypg Před 4 lety +1

      C most certainly *IS* strongly typed, but this is obscured by it providing access to the underlying "raw" or "machine hardware" types. It is the over-use of these "generic" types that weaken type use in C. The best way around it is to make heavy use of typedef to create separate, specific types for each need. See my comment further below for details why this is important (and free) to do.
      I agree about Hungarian Notation, but not for your reasons Every modern IDE will display the type of a variable by placing the cursor on/in/next-to the name: Let the IDE do its job! If your IDE lacks this feature, stop using it! I heartily recommend vscode (Visual Studio Code, code.visualstudio.com/, which confusingly is not "Microsoft Visual Studio") for pretty much every language on every development platform for every target system. Yes, vscode also speaks Arduino: github.com/Microsoft/vscode-arduino

  • @steenandreassen4591
    @steenandreassen4591 Před 4 lety +12

    Don't shorten this kind of instruction videos just because you feel it takes "Forever". The length is perfect !!

    • @gg-gn3re
      @gg-gn3re Před 4 lety

      Yea it's a good amount of info per minute. It isn't like a lot of teachers who will sit on a function for 15 minutes. Like at 33:00 he cruises through several things. a lot of teachers would sit and tell you what String() does for the 300th time

  • @georgef7754
    @georgef7754 Před 4 lety +6

    Using a config.h make much more sense to me now. Thank you.

  • @cloudsuck
    @cloudsuck Před 3 lety +2

    Thank you for the detailed code structure explanations. I've trying to learn how to modify sketches that I discover.
    Finally, the multiple tab variety is now clear!

  • @winandd8649
    @winandd8649 Před 10 měsíci

    OMG, this is exactly what I want to do with that sensor, thank you so much!

  • @pcruz9083
    @pcruz9083 Před 4 lety +1

    Outstanding!!! Definitely keep up doing these videos! Not only closes the loop on the project but also tell the choices/decisions made! Appreciate the hard work and percerverance necessary for putting all this together nicely.

  • @kabandajamir9844
    @kabandajamir9844 Před 2 lety

    The world's best teacher

  • @volodimirkun
    @volodimirkun Před rokem

    Hey, good Sir! Loved this tutorial!

  • @adiero
    @adiero Před 2 lety

    Thanks for that reminder at around 2/3. Found myself rapt with an empty ipa in hand following your code@ 0.75x, multiple rewinds. The reverse constant code style: It's weakly typed., so isn't there a risk of redefinition of the constant? or is the compiler our prude and catching that so well explained almost at the end. Want to build a biometric that senses alcohol consumption, net ppm in blood and count of ipa's openned .

  • @winandd8649
    @winandd8649 Před 9 měsíci

    I've built it, and with some tweaks it's working great now 👍
    I've added a button to be able to change the update interval on the fly.
    Also, I noted a bug in your code; The MQTT messages are spammed with dozens of messages per second.
    Reason;
    there is a check "if (true == g_pms_ppd_readings_taken)" which is neat, but after reporting, this variable has to be set to false again, which does not happen.
    The variable will become true again during the next reading, and then it's again one mqtt messages, etc..

    • @winandd8649
      @winandd8649 Před 9 měsíci

      I've altered lots of this in the source, but I think the mqtt message spamming is solved by adding these two lines at the end of the loop() function:
      g_pms_ppd_readings_taken = false;
      g_pms_ae_readings_taken = false;

  • @difegam3
    @difegam3 Před 4 lety +2

    Thanks..! very well explained..!

  • @ristomatti
    @ristomatti Před 4 lety +1

    Very thorough, well done!

  • @landspide
    @landspide Před 4 lety +1

    Lots of good C/C++ pointers!

  • @Hasitier
    @Hasitier Před 4 lety +1

    Interesting code explanation and really one of the most easy to read codes I’ve seen so far. I plan on making one of those but using a bigger oled display (one of those 2.42 inch I think they were) because my wife has a bad eyesight. Given your code this should not be to difficult I think. And of course I need to modify the case to fit that screen.

    • @SuperHouseTV
      @SuperHouseTV  Před 4 lety +1

      I look forward to seeing the result, that sounds like a great idea :-) I have a version with a 240x240 pixel colour TFT coming up in part 5.

  • @aaroncake
    @aaroncake Před 4 lety

    Thank you, this project is awesome! I have had a PMS5003 sitting around for a while however just haven't had the time to write the code. Well, you kind if did the exact thing I was planning on doing. :-) I'm going to port this code to Atmega and use Ethernet. Two minor suggestions though. For serial debugging, use the F("some text here") macro to store all those strings in flash, not RAM. It will save most of the RAM you use. Additionally avoid the String class and instead use cstrings. String had a tendency to fragment RAM and is a major source of memory leaks in the Arduino framework.

  • @jenskaa4044
    @jenskaa4044 Před 4 lety +3

    feedback for code (code compacted):
    ----
    For local variable in global scope prefix with static
    ----
    I would prefer to use an enum for the states:
    typedef enum {state1, state2, state3} states_t;
    static states_t mystate;
    ----
    For use struct for grouping:
    typedef struct {
    uint16_t pm1p0;
    uint16_t pm2p5;
    uint16_t pm10p0;
    } stdParticle_t;
    static stdParticle_t stdParticle; //is per default initialized to 0
    You should minimize the number of global variables. The stdParticle_t could be moved a "particle-sensor.h"
    ----
    g_mqtt_message_buffer[150]; Its normaly bad practice to use constants like 150 in the code , define the constants
    -----
    g_current_mode_button_state should not be global, is only used in a local function
    ----
    Separate the functionality i more files: example one for the particle-sensor "particle-sensor.cpp"
    make the updatePmsReadings() return a boolean to indicate if all readings been taken.

    • @SuperHouseTV
      @SuperHouseTV  Před 4 lety

      Great suggestions from someone who obviously knows what they're talking about. Which I don't.

    • @jenskaa4044
      @jenskaa4044 Před 4 lety

      @@SuperHouseTV I do not agree, you actually know what you are doing. Not many are following some code guideline like you do, in stead of throwing some code together. All code can be improved by having someone inspecting it and make feedback, and I thought that your code deserved it. So keep on improving and learning and follow some guidelines. You have succeeded if its easy to read your own in a year.
      Actually I am following your sensor project and waiting for my sensor to arrive in the near future. Looking forward to your next video.

  • @koenjanssen81
    @koenjanssen81 Před 4 lety +1

    Super interesting! Thanks for this video! I am currently finding my way with domoticz and want to use a few of these.

  • @andymouse
    @andymouse Před 4 lety

    Nice code description...cheers.

  • @TheEmbeddedHobbyist
    @TheEmbeddedHobbyist Před 4 lety +2

    With the case statement and default, the same can be said of the 'if' or 'else if' statement should always be completed by an 'ELSE'. Have you looked at Mirsa C coding standard. Great code review.

    • @SuperHouseTV
      @SuperHouseTV  Před 4 lety +1

      Good point. I haven't seen that coding standard, thanks for the tip. I'll check it out :-)

  • @JonathanWellskcender
    @JonathanWellskcender Před 4 lety +2

    Can't you define MQTT_MAX_PACKET_SIZE in your file above when you include the library and have it will override the PubSubClient value? The PubSubClient library looks to only use the default 128 value if MQTT_MAX_PACKET_SIZE hasn't been already defined.
    Great series of videos.

    • @SuperHouseTV
      @SuperHouseTV  Před 4 lety

      Wow, you may be right! I'm going to check that for sure. I thought it was impossible to overwrite because the Arduino pre-processor does the libraries first, but I really hope that's not the case.

    • @GilPeeters
      @GilPeeters Před 4 lety

      Was gonna say exactly the same thing. Have done that in the past. Use "--DMQTT_MAX_PACKET_SIZE=1024" to define it when running the compiler. Very easy when using Platform.io to compile/build. Not sure about the Arduino IDE

  • @n.r.2258
    @n.r.2258 Před 2 lety +1

    Is there any chance to use ESPHome to bring this software into the processor ?

  • @gg-gn3re
    @gg-gn3re Před 4 lety +2

    Does your "events" status topic get messages from every device on wakeup including waking from DEEPSLEEP? I did something similar (for my temperature sensors that go to sleep) but was looking at it sending messages every time from deepsleep and was curious if there's a way around that. As far as I can see there isn't since it's basically just rebooting it with the pin

    • @SuperHouseTV
      @SuperHouseTV  Před 4 lety +1

      Great question, and yes, that's what happens. If you have an RTC attached you could have that message happen conditionally, but the internal millis() counter is reset along with everything else during wakeup so you can't use that to check how long it's been. One nasty option is to ready millis() right before going to sleep, and add it to a runtime accumulator stored in flash. On wakeup, check how much runtime has been accumulated. Or instead of runtime, it could be a simple counter of how many reboots have been seen since the last report was sent. Clear and start the counter again when required. If anyone has a nice solution to this, I'd love to hear it.

    • @gg-gn3re
      @gg-gn3re Před 4 lety

      @@SuperHouseTV Ah I see, yea I think I read through something of a "hacky retain data through deepsleep" like that but it gave me nightmares so I just went with the msg every reset instead.. for now. RTC method is more understandable to me at least I might try it

  • @robertgallant6422
    @robertgallant6422 Před 4 lety +2

    Very well done, love this video & thank you for all the information, definitely going to help my coding. I do have one question about "Serial.begin(xxxx)". When you insert all these Serial.println in your code does it slow down the loop even if the board is not plugged in to the USB. Does it make any difference?

    • @SuperHouseTV
      @SuperHouseTV  Před 4 lety

      It would make a tiny difference having those in there, but nothing that you'd care about. It pushes the message into the serial buffer, which is never processed so the message doesn't go anywhere. It should run at the same speed whether USB is connected or not.

  • @User-Tal1951
    @User-Tal1951 Před 7 měsíci

    Is it possible to have measurements in two decimal points instead of only start from 1? For example 0.01 micro gram/m3

  • @RoSi4You
    @RoSi4You Před 3 lety

    *Super interesting. Really kicked me to do my own. Thank you!*
    Question: What needs to be changed, or can I use PMS7003 sensor without any changes in source please?

  • @TheEmbeddedHobbyist
    @TheEmbeddedHobbyist Před 4 lety +1

    Just another thought, main program has a loop that never ends, at 17:51 you call a client.loop(). in Arduino terms a loop never breaks it's the main running code. So calling a loop would appear to call a routine that will never come back to the calling process. Would it have given more clarity to the user if were to call a routing called something like, client.process_message_data().

    • @SuperHouseTV
      @SuperHouseTV  Před 4 lety +1

      That method is defined in the PubSubClient library, so it's not something that I named.

  • @robertsandy3794
    @robertsandy3794 Před 4 lety +1

    In you video, you said that you would leave a link to the Embedded C programming guide, however, I don't see it anywhere. Perhaps I have missed it

    • @SuperHouseTV
      @SuperHouseTV  Před 4 lety +2

      In the description is a link to the SuperHouse page for this video. On that page are links to resources, including the source code and the programming guide.

    • @robertsandy3794
      @robertsandy3794 Před 4 lety

      @@SuperHouseTV Got it, thanks

  • @Davet998
    @Davet998 Před 4 lety +1

    Great video, I picked up a few useful tips. Any reason why you used ESP.getChipId() and not WiFi.macAddress()
    ?

    • @SuperHouseTV
      @SuperHouseTV  Před 4 lety +1

      I don't have a strong preference, that would work fine too :-)

    • @flymypg
      @flymypg Před 4 lety

      @@SuperHouseTV The ESP MAC address can be set to any value desired! I would not assume anything about the uniqueness of it's value.
      Being able to set the MAC address in IoT devices greatly simplifies a couple of niche networking issues that most end-users don't care about, but that every single IoT device manufacturer on the planet most certainly DOES care about! For example, many vendor programs are able to discover their devices on your network even if they don't yet have IP addresses assigned. They do this by knowing the MAC address range they use for their devices, then using ICMP/ARP to scan through that MAC address space.
      In my own HA system, I move all MAC addresses **away** from the factory assigned value for that exact reason: I want to "own" my devices and limit their discovery by others. Any device lacking a modifiable MAC address gets returned. Still, I do record and preserve the original MAC address in case I return, sell or give the device away. This is far more work than most will want to do for themselves, but I'm paranoid about security, to the point that it's more of an addictive game for me.
      As an example, my router sends me an email whenever a new MAC address shows up on my network. If it is on my wired network, that MAC address is immediately blocked at the switch until I explicitly add it to the "permitted" list. There is no need for this on WiFi, which has decent security (when used correctly).

  • @AhmedBahgat
    @AhmedBahgat Před 4 lety

    How are you mate, I really appreciate the information you post and the way you do it. thank you very much, I hope you can give some insight on an idea that I want to make, my garage door is fully sonoffed using sonoff sv flashed with ESPhome with an addition of an ultrasonic sensor to detect if the car is parked or not. all work great and as expected but I need to add a very important safety measure, I want the door to stop functioning if an object is in its way, like my pet crosiing the garage door while it is operating or my car is parked under the door but not fully inside the garage, I want to prevent the door hitting the car if the remote is activated by accident, can I do that with what I have already, like some configuration inside the sonoff sv bearing in mind I have already used GPIO 4, 5 and 14. Or do I need to add a sonoff basic so its relay stops the garage motor from working based on another ultrasonic sensor reading that measures the door horizontal gap? and if so how, please? Your insight would be greatly appreciated. Have a nice day

  • @EsotericArctos
    @EsotericArctos Před 4 lety +1

    Just curious. What is the difference between your "Hello World" alive message and using a LWT?

    • @SuperHouseTV
      @SuperHouseTV  Před 4 lety +1

      Good question. The LWT is intended for a very different purpose: it's to determine what happens when a device disconnects from the broker, not when it first connects. I'm using the "status" topic kind of like a system log.

    • @EsotericArctos
      @EsotericArctos Před 4 lety

      @@SuperHouseTV Thanks for that. Makes sense.

  • @hailnora
    @hailnora Před 4 lety +10

    Sneaky inside out shirt change

    • @gg-gn3re
      @gg-gn3re Před 4 lety +1

      I once wore pants inside out for like 20 hours in my shop before noticing lmao.

    • @DodgyBrothersEngineering
      @DodgyBrothersEngineering Před 4 lety +1

      lol got me, didn't even pick up on it. I was too busy admiring the Rembrandt on the wall.

  • @iulianch
    @iulianch Před 4 lety +1

    Why you didn’t add the user and password for MQTT? In my home assistant I have it with user and password. Thank you for the video, very nice!!!

    • @SuperHouseTV
      @SuperHouseTV  Před 4 lety +1

      No particular reason, I just didn't do it in this example. I should add them as options. It's a good idea.

  • @user-0xastalavista
    @user-0xastalavista Před 4 lety

    Hey I notice your shirt... :-)
    Great video anyway.
    Thank you.

  • @koenjanssen81
    @koenjanssen81 Před 4 lety

    I hope someone can help me out.......when i try to compile in arduino IDE i get a lot of error messages, the first being line 122 and the error is: 'WifiClient' does not name a type
    Does anyone know how i can fix this?

  • @DrGreenGiant
    @DrGreenGiant Před 4 lety +1

    Curious why you use #defines for your state machine states rather than an enum (or my preferred an enum class and a switch case)? Achieves the same thing of course, just interested!

    • @SuperHouseTV
      @SuperHouseTV  Před 4 lety

      No specific reason. I've used enum for this purpose other times. I'm just fickle. I'm also not a programmer, so I'm fudging my way through things.

  • @flymypg
    @flymypg Před 4 lety

    Some minor nits:
    1. Having uint32_t and similar "naked" types everywhere makes it seem all such variables are "the same thing". They are not the same thing! They only share a common underlying representation, with very different meanings. That is, the difference between syntax and semantics. Liberal use of typedefs can make code not only more readable (especially improved function signatures), but also give the compiler more hints to help catch incorrect assignments, and also force the programmer to use explicit casts when intentionally breaking type boundaries, even when the underlying machine type may be the same. And it comes with no cost to the final code speed or size (it's free!).
    2. #defines are EVIL! Especially when used for machine states. Enums are much more practical and type-safe, and again it lets the compiler help keep things correct, keeping like combined with like. Again, at no cost to the final code speed or size (it's free!).
    3. Avoid using naked 'if' tests ("if (x)..."), where the reader of the code must remember what the underlying representation of true and false are in the current context. This becomes very problematic when multiple languages are used. For example, I use C for tiny embedded systems, and Python for large-scale data analysis from suites of tiny systems. A better approach is to ALWAYS explicitly use a logical comparison operator (>, =,

  • @eswarnichtsmehrfrei
    @eswarnichtsmehrfrei Před 4 lety

    we are migrating from Arduino to VSCode with platform.io

  • @robertowembley3454
    @robertowembley3454 Před 3 lety

    Whoever smelt it delt it