Mini capteur de lumière et de choc | nRF52840

Dans l'article d'aujourd'hui, je veux parler d'un nouveau capteur de lumière et de vibrations. Le capteur fonctionne sur le module E73-2G4M08S1C (nRF52840). La raison du développement de MK était l'ajout assez simple de support à l'Arduino IDE basé sur la bibliothèque Sandeep Mistry, son faible coût, ses excellentes fonctionnalités et la possibilité de passer ensuite de manière transparente de MySensors à ZigBee par exemple :)



La nécessité d'un tel projet est apparue avec l'avènement d'un store à enrouleur dans ma maison :). Il était nécessaire que le contrôleur de volet roulant reçoive des données sur le niveau d'éclairage directement à partir de la fenêtre sur laquelle le rideau est contrôlé par le contrôleur.

Initialement, j'avais prévu d'utiliser le capteur BH1750FVI, les caractéristiques de ce capteur étaient fines, il a été utilisé plus d'une fois dans mes autres projets de bricolage. Mais à un moment donné, en discutant de toutes sortes de capteurs, quelqu'un a suggéré de regarder de plus près le capteur MAX44009. J'ai regardé de près et à partir de ce moment, je ne me suis plus jamais souvenu du BH1750FVI.



Spécifications du MAX44009:

  • Plage de tension d'alimentation 1,7-3,6 V,
  • Courant de fonctionnement ultra-faible - 0,65 μA (inférieur au courant en mode Power Down pour de nombreux produits similaires),
  • Large plage dynamique 22 bits 0,045-188000,
  • La présence de la fonction d'interruption, la possibilité de surveiller en continu le niveau d'éclairage et de générer un signal d'interruption pour le MC en cas de dépassement des seuils spécifiés.

Je voulais aussi l'universalité ou une sorte de fonctionnalité avancée, après un peu de réflexion, j'ai décidé d'ajouter un accéléromètre au projet qui fonctionnerait comme un capteur de vibrations. Cela ajoutera des fonctionnalités de sécurité au capteur. Dans mon cas, sur la base de l'événement, un script en UD sera déclenché à partir du capteur qui simule la présence dans la maison (allumez la lumière dans les pièces), lorsqu'il est activé, personne n'est à la maison. En général, le capteur peut également être utilisé comme capteur autonome de vibrations et de chocs, seul un capteur de lumière ou uniquement un accéléromètre peut être soudé. L'accéléromètre peut également être reprogrammé pour reconnaître les tapas, les virages, etc.

Comme accéléromètre, c'est le capteur LIS2DW12 qui a été choisi, c'est l'un des accéléromètres les plus économiques du marché, sinon le plus économique.



Caractéristiques de LIS2DW12:

  • Plage de tension d'alimentation 1,62-3,6 V,
  • 50 nA en mode veille
  • 1 μA en mode basse consommation,
  • La présence de la fonction d'interruption, la possibilité d'un fonctionnement continu du capteur et la formation d'un signal d'interruption pour le MC en cas de dépassement des seuils spécifiés.

Anticipant d'éventuels problèmes pour les frères Arduino avec le lancement de ce modèle d'accéléromètre en raison du manque total de bibliothèques Arduino prêtes à l'emploi pour LIS2DW12, un support a ensuite été ajouté pour un autre modèle d'accéléromètre LIS2DH12 avec des caractéristiques similaires, mais une consommation plus élevée en mode basse consommation - 2 μA. Pour les accéléromètres LIS2DH12, il existe de très bonnes bibliothèques Arduino.

Le capteur de lumière est alimenté par une pile CR2032.



Beaucoup de ceux qui utilisent déjà le projet MySensors pour construire leur maison intelligente connaissent probablement la logique non optimale de MySensors sur les appareils à batterie. Envoi constant de présentations lors du redémarrage de l'appareil, mode sous-optimal de récupération automatique des appareils sur le réseau, consommation sous-optimale avec des interruptions activées dans la fonction sommeil, en général, la présence même de seulement deux interruptions dans un rêve. Toutes les circonstances suggèrent que les fondateurs du projet Maysensors ont une mauvaise attitude envers les batteries :)

Dans ce projet, j'ai essayé de minimiser ces lacunes.

La chose la plus fondamentale à laquelle je voulais faire face était les interruptions. En règle générale, lorsque des interruptions sont activées dans la fonction de veille, le nRF52 MK commence à consommer + 10 mKA par rapport à la consommation du MK lui-même (de 1,4 μA pour nRF52810-52811 et jusqu'à 4,7 μA pour nRF52832-52840). Un total de 2 interruptions sont disponibles.

int8_t sleep(const uint8_t interrupt1, const uint8_t mode1, const uint8_t interrupt2, const uint8_t mode2, const uint32_t sleepingMS = 0, const bool smartSleep = false); 

Plus tôt dans la recherche de solutions pour optimiser la consommation dans un rêve, la possibilité d'utiliser des interruptions sur le comparateur intégré à faible consommation a été réalisée, la consommation dans un rêve n'était plus de + 10 μA, mais de l'ordre de +1 μA, mais il n'était possible d'utiliser qu'une seule interruption. En principe, en dehors des inconvénients du code (traitement des signaux provenant de broches supplémentaires) et des éléments supplémentaires sur la carte (diodes), c'était une bonne solution. Mais je le voulais encore mieux.

À cette époque, j'étais déjà un peu familier :) avec le SDK nRF5, donc après avoir étudié le forum mysensors.org (astuces, conseils :)), j'ai décidé de chercher la solution dans le SDK nRF5 ... et je l'ai trouvée. Maintenant, un nombre illimité d'interruptions est disponible qui ne consomme pratiquement rien de plus. La principale chose à faire est d'apporter une petite modification au code de la bibliothèque Sandeepmistry nRF5, au fichier WInterrupts. Avant la fonction GPIOTE_IRQHandler () ajoutez l'attribut "faible" qui vous permettra de redéfinir cette fonction de bibliothèque en code utilisateur - __attribute__ ((faible))



Le fichier se trouve sur le chemin - C: \ Users \ COMPUTER_USER \ AppData \ Local \ Arduino15 \ packages \ sandeepmistry \
matériel \ nRF5 \ 0.6.0 \ cores \ nRF5 \
Les fichiers qui doivent être ajoutés au projet se trouvent sur ma Gita dans le dossier EFEKTA-LIS2DW12-MAX44009-E73C / SOURCE CODE / ARDUINO /, un exemple d'utilisation est dans l'esquisse EFEKTA-LIS2DW12-MAX44009-E73C / SOURCE CODE / ARDUINO / vibro_ambi1_ino

La prochaine chose que je voulais finaliser était la bibliothèque Sandeepmistry nRF5 elle-même, ce qui serait pratique pour travailler avec les nouveaux MK nRF52840, nRF52811 et anciens, mais pour une raison quelconque, sans ajouter sans raison nRF52810 (après tout, 1,4 μA dans un rêve n'est pas pour vous :)). Bien sûr, vous pouvez travailler avec nRF52840 dans l'IDE Arduino et sous nRF52832, mais ... je voulais le rendre plus confortable. Ci-dessus, j'ai écrit qu'il y avait un intérêt à faire quelque chose sur le Nordic SDK, en particulier Segger Embedded Studio est gratuit lorsque vous travaillez avec MK nRF5. La prise en charge de toutes les cartes qui m'intéressent a été prise du SDK et transférée à Arduino (jusqu'à présent sans softdevice, et ce n'est pas nécessaire avec mysensors). De nouvelles cartes ont également été ajoutées à la bibliothèque MySensors.



github.com/smartboxchannel/arduino-nRF5
github.com/smartboxchannel/MySensors

Si l'un des lecteurs connaît déjà mes articles précédents, vous ne serez probablement pas surpris que j'ai, comme auparavant, fabriqué une imprimante à polymère liquide sur le SLA avec un étui pour un capteur de lumière. Les avantages de l'impression sur cette technologie sont sa grande précision. Mais bien sûr, il y a aussi des inconvénients, les polymères avec lesquels les imprimantes SLA domestiques sont capables de travailler sont toujours de résistance inférieure aux plastiques pour FDM.Le modèle 3D du boîtier se compose de 2 parties, chaque moitié a été imprimée pendant 40 minutes (épaisseur de couche 50 microns). Surtout, il n'est probablement pas nécessaire de s'arrêter là, juste quelques images du processus de développement dans un éditeur 3D.







Le programme du capteur a implémenté un redémarrage de l'appareil sans envoyer de présentations. Cela fonctionne comme ceci: alors que l'appareil est nouveau et non ajouté à votre réseau, lorsqu'il est ajouté au réseau pour la première fois, il enregistrera et terminera la présentation, une fois l'enregistrement réussi, l'appareil qui a reçu l'identifiant n'enverra plus la présentation, mais vous pouvez envoyer la présentation en cliquant sur le bouton du capteur (ce nécessaire, par exemple, si tous les capteurs n'ont pas été présentés avec succès immédiatement). De plus, le programme désactive la possibilité d'une récupération automatique standard de l'activité réseau (si le capteur perd le réseau), sa propre option non standard a été inventée :). Cela fonctionne comme ceci: si le capteur détecte plus de 5 tentatives (ce qui est configurable) infructueuses pour envoyer des messages dans une rangée, l'appareil cesse d'envoyer des données à partir des capteurs et commence à envoyer un message de recherche réseau avec un intervalle initialement égal à l'intervalle d'envoi depuis / 2 capteurs et en augmentant périodiquement cet intervalle c chaque envoi, entre envois un rêve sain. Tout cela vous permet d'économiser considérablement la batterie.

Code de test
 // SDK PORT extern "C" { #include "app_gpiote.h" #include "nrf_gpio.h" } #define APP_GPIOTE_MAX_USERS 1 #include <LIS2DW12Sensor.h> #include <MAX44009.h> //#define MY_DEBUG #define MY_RADIO_NRF5_ESB #define MY_DISABLED_SERIAL int16_t mtwr; #define MY_TRANSPORT_WAIT_READY_MS (mtwr) #define MY_NRF5_ESB_PA_LEVEL (NRF5_PA_MAX) #include <MySensors.h> #define SN "LUX & VIBRO SENS" #define SV "1.0" #define V_SENS_CHILD_ID 1 #define LUX_SENS_CHILD_ID 2 #define WPM_SENS_CHILD_ID 3 #define INTERVAL_R_LUX_CHILD_ID 220 #define LEVEL_SENSIV_V_SENS_CHILD_ID 230 #define ENABLE_WPM_SENS_CHILD_ID 240 #define SIGNAL_Q_ID 253 #define TEMP_CHILD_ID 254 //for any tests #include <MySensors.h> MyMessage vibroMsg(V_SENS_CHILD_ID, V_TRIPPED); MyMessage brightMsg(LUX_SENS_CHILD_ID, V_LEVEL); MyMessage wpmMsg(WPM_SENS_CHILD_ID, V_LEVEL); MyMessage conf_wpmMsg(ENABLE_WPM_SENS_CHILD_ID, V_VAR1); MyMessage conf_vsensMsg(LEVEL_SENSIV_V_SENS_CHILD_ID, V_VAR1); MyMessage conf_interv_rluxMsg(INTERVAL_R_LUX_CHILD_ID, V_VAR1); MyMessage tempMsg(TEMP_CHILD_ID, V_VAR1); //for any tests bool nosleep = 0; bool button_flag = 0; bool configMode = 0; bool wpm_enable = 0; bool onoff = 1; bool flag_update_transport_param; bool flag_sendRoute_parent; bool flag_no_present; bool flag_nogateway_mode; bool flag_find_parent_process; bool flag_fcount; bool Ack_TL; bool Ack_FP; bool PRESENT_ACK; byte conf_vibro_set = 1; byte interval_reading_lux = 10; byte err_delivery_beat; byte problem_mode_count; uint8_t countbatt = 0; uint8_t batt_cap; uint8_t old_batt_cap = 100; //unsigned long BATT_TIME = 43200000; //12 hours uint32_t BATT_TIME = 7200000; //12 hours uint32_t SLEEP_TIME_TEMP = 60000; //1 minute uint32_t SLEEP_TIME; uint32_t C_BATT_TIME; uint32_t oldmillis; uint32_t newmillis; uint32_t previousMillis; uint32_t lightMillisR; uint32_t configMillis; uint32_t interrupt_time; uint32_t SLEEP_TIME_W; uint32_t axel_time; int16_t result; int16_t brightness; int16_t lastbrightness; int16_t brightThreshold = 25; int16_t myid; int16_t mypar; int16_t old_mypar = -1; int16_t master_id; float Wpm; float ODR_1Hz6_LP_ONLY = 1.6f; float ODR_12Hz5 = 12.5f; float ODR_25Hz = 25.0f; float ODR_50Hz = 50.0f; float ODR_100Hz = 100.0f; float ODR_200Hz = 200.0f; bool vibro = 1; static app_gpiote_user_id_t m_gpiote_user_id; uint32_t PIN_BUTTON1_MASK; uint32_t AXEL_INT1_MASK; volatile byte axelInt1Status = 0; volatile byte buttInt1Status = 0; uint16_t batteryVoltage; int16_t linkQuality; int16_t old_linkQuality; LIS2DW12Sensor *lis2; MAX44009 light; void preHwInit() { board_Init(); } void before() { blinky(1, 1, GREEN_LED); wait(1000); nRF_Init(); device_Conf(); happy_init(); } void setup() { interrupt_Init(); sensors_Init(); config_Happy_node(); } void presentation() { if (!sendSketchInfo(SN, SV)) { _transportSM.failedUplinkTransmissions = 0; sleep(1000); wait(50); if (!sendSketchInfo(SN, SV)) { _transportSM.failedUplinkTransmissions = 0; } } present(V_SENS_CHILD_ID, S_VIBRATION, "STATUS VIBRO", 1); wait(2500, C_PRESENTATION, S_VIBRATION); CORE_DEBUG(PSTR("MyS: TEST WAIT AFTER PRESENT SENSOR\n")); if (PRESENT_ACK == 0) { _transportSM.failedUplinkTransmissions = 0; sleep(1500); wait(50); present(V_SENS_CHILD_ID, S_VIBRATION, "STATUS VIBRO", 1); wait(2500, C_PRESENTATION, S_VIBRATION); CORE_DEBUG(PSTR("MyS: TEST WAIT AFTER PRESENT SENSOR\n")); _transportSM.failedUplinkTransmissions = 0; } else { PRESENT_ACK = 0; } present(LUX_SENS_CHILD_ID, S_LIGHT_LEVEL, "LUX", 1); wait(2500, C_PRESENTATION, S_LIGHT_LEVEL); CORE_DEBUG(PSTR("MyS: TEST WAIT AFTER PRESENT SENSOR\n")); if (PRESENT_ACK == 0) { _transportSM.failedUplinkTransmissions = 0; sleep(1500); wait(50); present(LUX_SENS_CHILD_ID, S_LIGHT_LEVEL, "LUX", 1); wait(2500, C_PRESENTATION, S_LIGHT_LEVEL); CORE_DEBUG(PSTR("MyS: TEST WAIT AFTER PRESENT SENSOR\n")); _transportSM.failedUplinkTransmissions = 0; } else { PRESENT_ACK = 0; } present(WPM_SENS_CHILD_ID, S_LIGHT_LEVEL, "W/M^2", 1); wait(2500, C_PRESENTATION, S_LIGHT_LEVEL); CORE_DEBUG(PSTR("MyS: TEST WAIT AFTER PRESENT SENSOR\n")); if (PRESENT_ACK == 0) { _transportSM.failedUplinkTransmissions = 0; sleep(1500); wait(50); present(WPM_SENS_CHILD_ID, S_LIGHT_LEVEL, "W/M^2", 1); wait(2500, C_PRESENTATION, S_LIGHT_LEVEL); CORE_DEBUG(PSTR("MyS: TEST WAIT AFTER PRESENT SENSOR\n")); _transportSM.failedUplinkTransmissions = 0; } else { PRESENT_ACK = 0; } present(SIGNAL_Q_ID, S_CUSTOM, "SIGNAL QUALITY", 1); wait(2500, C_PRESENTATION, S_CUSTOM); CORE_DEBUG(PSTR("MyS: TEST WAIT AFTER PRESENT SENSOR\n")); if (PRESENT_ACK == 0) { _transportSM.failedUplinkTransmissions = 0; sleep(1500); wait(50); present(SIGNAL_Q_ID, S_CUSTOM, "SIGNAL QUALITY", 1); wait(2500, C_PRESENTATION, S_CUSTOM); CORE_DEBUG(PSTR("MyS: TEST WAIT AFTER PRESENT SENSOR\n")); _transportSM.failedUplinkTransmissions = 0; } else { PRESENT_ACK = 0; } present(ENABLE_WPM_SENS_CHILD_ID, S_CUSTOM, "ON|OFF WPM", 1); wait(2500, C_PRESENTATION, S_CUSTOM); CORE_DEBUG(PSTR("MyS: TEST WAIT AFTER PRESENT SENSOR\n")); if (PRESENT_ACK == 0) { _transportSM.failedUplinkTransmissions = 0; sleep(1500); wait(50); present(ENABLE_WPM_SENS_CHILD_ID, S_CUSTOM, "ON|OFF WPM", 1); wait(2500, C_PRESENTATION, S_CUSTOM); CORE_DEBUG(PSTR("MyS: TEST WAIT AFTER PRESENT SENSOR\n")); _transportSM.failedUplinkTransmissions = 0; } else { PRESENT_ACK = 0; } present(LEVEL_SENSIV_V_SENS_CHILD_ID, S_CUSTOM, "SENS LEVEL VIBRO", 1); wait(2500, C_PRESENTATION, S_CUSTOM); CORE_DEBUG(PSTR("MyS: TEST WAIT AFTER PRESENT SENSOR\n")); if (PRESENT_ACK == 0) { _transportSM.failedUplinkTransmissions = 0; sleep(1500); wait(50); present(LEVEL_SENSIV_V_SENS_CHILD_ID, S_CUSTOM, "SENS LEVEL VIBRO", 1); wait(2500, C_PRESENTATION, S_CUSTOM); CORE_DEBUG(PSTR("MyS: TEST WAIT AFTER PRESENT SENSOR\n")); _transportSM.failedUplinkTransmissions = 0; } else { PRESENT_ACK = 0; } present(INTERVAL_R_LUX_CHILD_ID, S_CUSTOM, "INTERVAL RLUX|MIN", 1); wait(2500, C_PRESENTATION, S_CUSTOM); CORE_DEBUG(PSTR("MyS: TEST WAIT AFTER PRESENT SENSOR\n")); if (PRESENT_ACK == 0) { _transportSM.failedUplinkTransmissions = 0; sleep(1500); wait(50); present(INTERVAL_R_LUX_CHILD_ID, S_CUSTOM, "INTERVAL RLUX|MIN", 1); wait(2500, C_PRESENTATION, S_CUSTOM); CORE_DEBUG(PSTR("MyS: TEST WAIT AFTER PRESENT SENSOR\n")); _transportSM.failedUplinkTransmissions = 0; } else { PRESENT_ACK = 0; } send(conf_wpmMsg.set(wpm_enable), 1); wait(2500, C_SET, V_VAR1); CORE_DEBUG(PSTR("MyS: TEST WAIT AFTER SEND CONF SENSOR\n")); if (Ack_TL == 0) { _transportSM.failedUplinkTransmissions = 0; sleep(1500); wait(50); send(conf_wpmMsg.set(wpm_enable), 1); wait(2500, C_SET, V_VAR1); CORE_DEBUG(PSTR("MyS: TEST WAIT AFTER SEND CONF SENSOR\n")); _transportSM.failedUplinkTransmissions = 0; } else { Ack_TL = 0; } send(conf_vsensMsg.set(conf_vibro_set), 1); wait(2500, C_SET, V_VAR1); CORE_DEBUG(PSTR("MyS: TEST WAIT AFTER SEND CONF SENSOR\n")); if (Ack_TL == 0) { _transportSM.failedUplinkTransmissions = 0; sleep(1500); wait(50); send(conf_vsensMsg.set(conf_vibro_set), 1); wait(2500, C_SET, V_VAR1); CORE_DEBUG(PSTR("MyS: TEST WAIT AFTER SEND CONF SENSOR\n")); _transportSM.failedUplinkTransmissions = 0; } else { Ack_TL = 0; } send(conf_interv_rluxMsg.set(interval_reading_lux), 1); wait(2500, C_SET, V_VAR1); CORE_DEBUG(PSTR("MyS: TEST WAIT AFTER SEND CONF SENSOR\n")); if (Ack_TL == 0) { _transportSM.failedUplinkTransmissions = 0; sleep(1500); wait(50); send(conf_interv_rluxMsg.set(interval_reading_lux), 1); wait(2500, C_SET, V_VAR1); CORE_DEBUG(PSTR("MyS: TEST WAIT AFTER SEND CONF SENSOR\n")); _transportSM.failedUplinkTransmissions = 0; } else { Ack_TL = 0; } } void loop() { if (flag_update_transport_param == 1) { update_Happy_transport(); } if (flag_sendRoute_parent == 1) { present_only_parent(); } if (isTransportReady() == true) { if (flag_nogateway_mode == 0) { if (flag_find_parent_process == 1) { find_parent_process(); } if (configMode == 0) { if ((axelInt1Status == AXEL_INT1) || (buttInt1Status == PIN_BUTTON1)) { if (axelInt1Status == AXEL_INT1) { nosleep = 1; send_Axel(); axelInt1Status = 0; newmillis = millis(); interrupt_time = newmillis - oldmillis; SLEEP_TIME_W = SLEEP_TIME_W - interrupt_time; if (SLEEP_TIME_W < 5000) { SLEEP_TIME_W = SLEEP_TIME; send_Brigh(1); countbatt++; if (countbatt == C_BATT_TIME) { sendBatteryStatus(1); countbatt = 0; } } nosleep = 0; } if (buttInt1Status == PIN_BUTTON1) { if (digitalRead(PIN_BUTTON1) == 0 && button_flag == 0) { button_flag = 1; nosleep = 1; previousMillis = millis(); ledsOff(); } if (digitalRead(PIN_BUTTON1) == 0 && button_flag == 1) { if ((millis() - previousMillis > 0) && (millis() - previousMillis <= 1750)) { if (millis() - lightMillisR > 25) { lightMillisR = millis(); onoff = !onoff; digitalWrite(GREEN_LED, onoff); } } if ((millis() - previousMillis > 1750) && (millis() - previousMillis <= 2000)) { ledsOff(); } if ((millis() - previousMillis > 2000) && (millis() - previousMillis <= 4000)) { if (millis() - lightMillisR > 25) { lightMillisR = millis(); onoff = !onoff; digitalWrite(BLUE_LED, onoff); } } if ((millis() - previousMillis > 4000) && (millis() - previousMillis <= 4250)) { ledsOff(); } if ((millis() - previousMillis > 4250) && (millis() - previousMillis <= 6250)) { if (millis() - lightMillisR > 25) { lightMillisR = millis(); onoff = !onoff; digitalWrite(RED_LED, onoff); } } if ((millis() - previousMillis > 6250) && (millis() - previousMillis <= 6500)) { ledsOff(); } if ((millis() - previousMillis > 6500) && (millis() - previousMillis <= 8500)) { if (millis() - lightMillisR > 50) { lightMillisR = millis(); onoff = !onoff; digitalWrite(RED_LED, onoff); } } if (millis() - previousMillis > 8500) { ledsOff(); } } if (digitalRead(PIN_BUTTON1) == 1 && button_flag == 1) { if (millis() - previousMillis <= 2000) { ledsOff(); send_Brigh(0); nosleep = 0; button_flag = 0; buttInt1Status = 0; } if ((millis() - previousMillis > 2000) && (millis() - previousMillis <= 4000)) { ledsOff(); configMode = 1; button_flag = 0; configMillis = millis(); } if ((millis() - previousMillis > 4250) && (millis() - previousMillis <= 6250)) { ledsOff(); blinky(2, 2, RED_LED); button_flag = 0; buttInt1Status = 0; presentation(); nosleep = 0; } if ((millis() - previousMillis > 6500) && (millis() - previousMillis <= 8500)) { ledsOff(); blinky(3, 3, RED_LED); new_device(); } if (((millis() - previousMillis > 1750) && (millis() - previousMillis <= 2000)) || ((millis() - previousMillis > 4000) && (millis() - previousMillis <= 4250)) || ((millis() - previousMillis > 6250) && (millis() - previousMillis <= 6500)) || ((millis() - previousMillis > 8500))) { ledsOff(); blinky(1, 2, GREEN_LED); nosleep = 0; button_flag = 0; buttInt1Status = 0; } } } } else { SLEEP_TIME_W = SLEEP_TIME; send_Brigh(1); countbatt++; if (countbatt == C_BATT_TIME) { sendBatteryStatus(1); countbatt = 0; } nosleep = 0; } } else { if (millis() - configMillis > 30000) { blinky(3, 3, GREEN_LED); configMode = 0; nosleep = 0; button_flag = 0; buttInt1Status = 0; } } } else { if (buttInt1Status == PIN_BUTTON1) { if (digitalRead(PIN_BUTTON1) == 0 && button_flag == 0) { button_flag = 1; nosleep = 1; previousMillis = millis(); ledsOff(); } if (digitalRead(PIN_BUTTON1) == 0 && button_flag == 1) { if ((millis() - previousMillis > 0) && (millis() - previousMillis <= 500)) { ledsOff(); } if ((millis() - previousMillis > 500) && (millis() - previousMillis <= 2500)) { lightMillisR = millis(); onoff = !onoff; digitalWrite(BLUE_LED, onoff); } if ((millis() - previousMillis > 2500) && (millis() - previousMillis <= 2750)) { ledsOff(); } if ((millis() - previousMillis > 2750) && (millis() - previousMillis <= 4750)) { if (millis() - lightMillisR > 50) { lightMillisR = millis(); onoff = !onoff; digitalWrite(RED_LED, onoff); } } if (millis() - previousMillis > 4750) { ledsOff(); blinky(3, 1, GREEN_LED); button_flag = 0; nosleep = 0; buttInt1Status = 0; } } if (digitalRead(PIN_BUTTON1) == 1 && button_flag == 1) { if (millis() - previousMillis <= 500) { ledsOff(); button_flag = 0; nosleep = 0; buttInt1Status = 0; } if ((millis() - previousMillis > 500) && (millis() - previousMillis <= 2500)) { ledsOff(); blinky(1, 1, BLUE_LED); check_parent(); button_flag = 0; nosleep = 0; buttInt1Status = 0; } if ((millis() - previousMillis > 2500) && (millis() - previousMillis <= 2750)) { ledsOff(); button_flag = 0; nosleep = 0; buttInt1Status = 0; } if ((millis() - previousMillis > 2750) && (millis() - previousMillis <= 4750)) { ledsOff(); blinky(3, 3, RED_LED); new_device(); } if (millis() - previousMillis > 4750) { ledsOff(); button_flag = 0; nosleep = 0; buttInt1Status = 0; } } } else { check_parent(); } } } if (_transportSM.failureCounter > 0) { _transportConfig.parentNodeId = loadState(101); _transportConfig.nodeId = myid; _transportConfig.distanceGW = loadState(103); mypar = _transportConfig.parentNodeId; nosleep = 0; flag_fcount = 1; err_delivery_beat = 5; happy_node_mode(); gateway_fail(); } if (configMode == 0) { if (nosleep == 0) { oldmillis = millis(); axelInt1Status = 0; buttInt1Status = 0; wait(100); sleep(SLEEP_TIME_W, false); wait(50); nosleep = 1; } } } float GetWpm() { float SunLuxCoef = 0.0079; float Wpm_temp = 0; Wpm_temp = (float)brightness; Wpm_temp *= SunLuxCoef; return Wpm_temp; } void blinky(uint8_t pulses, uint8_t repit, uint8_t ledColor) { for (int x = 0; x < repit; x++) { if (x > 0) { sleep(300); } for (int i = 0; i < pulses; i++) { if (i > 0) { sleep(80); } digitalWrite(ledColor, LOW); sleep(20); digitalWrite(ledColor, HIGH); } } } void ledsOff() { digitalWrite(RED_LED, HIGH); digitalWrite(GREEN_LED, HIGH); digitalWrite(BLUE_LED, HIGH); } void nRF_Init() { NRF_POWER->DCDCEN = 1; NRF_NFCT->TASKS_DISABLE = 1; NRF_NVMC->CONFIG = 1; NRF_UICR->NFCPINS = 0; NRF_NVMC->CONFIG = 0; NRF_SAADC ->ENABLE = 0; NRF_PWM0 ->ENABLE = 0; NRF_PWM1 ->ENABLE = 0; NRF_PWM2 ->ENABLE = 0; NRF_TWIM1 ->ENABLE = 0; NRF_TWIS1 ->ENABLE = 0; NRF_RADIO->TXPOWER = 8; } void sensors_Init() { Wire.begin(); wait(100); light.begin(); wait(100); lis2 = new LIS2DW12Sensor (&Wire); vibro_Init(); if (isTransportReady() == true) { blinky(3, 1, BLUE_LED); wait(200); blinky(3, 1, GREEN_LED); wait(200); blinky(3, 1, RED_LED); SLEEP_TIME_W = SLEEP_TIME; send_Brigh(0); wait(50); sendBatteryStatus(0); axel_time = millis(); } else { blinky(5, 3, RED_LED); } } void vibro_Init() { if (conf_vibro_set == 1) { lis2->ODRTEMP = ODR_1Hz6_LP_ONLY; } if (conf_vibro_set == 2) { lis2->ODRTEMP = ODR_12Hz5; } if (conf_vibro_set == 3) { lis2->ODRTEMP = ODR_25Hz; } if (conf_vibro_set == 4) { lis2->ODRTEMP = ODR_100Hz; } if (conf_vibro_set == 5) { lis2->ODRTEMP = ODR_200Hz; } lis2->Enable_X(); wait(50); lis2->Enable_Wake_Up_Detection(); wait(50); } void board_Init() { pinMode(PIN_BUTTON1, INPUT); pinMode(AXEL_INT1, INPUT); pinMode(AXEL_INT2, INPUT); pinMode(AMBI_INT, INPUT); pinMode(RED_LED, OUTPUT); pinMode(GREEN_LED, OUTPUT); pinMode(BLUE_LED, OUTPUT); ledsOff(); } void send_Axel() { if (millis() - axel_time >= 5000) { blinky(6, 1, RED_LED); lis2->Disable_Wake_Up_Detection(); wait(100); if (_transportConfig.parentNodeId == 0) { if (send(vibroMsg.set(vibro))) { wait(100); err_delivery_beat = 0; if (flag_nogateway_mode == 1) { flag_nogateway_mode = 0; CORE_DEBUG(PSTR("MyS: NORMAL GATEWAY MODE\n")); err_delivery_beat = 0; } } else { _transportSM.failedUplinkTransmissions = 0; if (err_delivery_beat < 5) { err_delivery_beat++; } if (err_delivery_beat == 4) { if (flag_nogateway_mode == 0) { gateway_fail(); CORE_DEBUG(PSTR("MyS: LOST GATEWAY MODE\n")); } } } lis2->Enable_Wake_Up_Detection(); wait(100); axel_time = millis(); nosleep = 0; } if (_transportConfig.parentNodeId > 0) { send(vibroMsg.set(vibro), 1); wait(2500, C_SET, V_TRIPPED); if (Ack_TL == 1) { Ack_TL = 0; err_delivery_beat = 0; //sleep_flag = 0; if (flag_nogateway_mode == 1) { flag_nogateway_mode = 0; CORE_DEBUG(PSTR("MyS: NORMAL GATEWAY MODE\n")); err_delivery_beat = 0; } } else { _transportSM.failedUplinkTransmissions = 0; if (err_delivery_beat < 5) { err_delivery_beat++; } if (err_delivery_beat == 4) { if (flag_nogateway_mode == 0) { gateway_fail(); CORE_DEBUG(PSTR("MyS: LOST GATEWAY MODE\n")); } } } lis2->Enable_Wake_Up_Detection(); wait(100); axel_time = millis(); nosleep = 0; } } else { nosleep = 0; } } void send_Brigh(bool start) { brightness = light.get_lux() * 2; wait(50); if (start == 1) { if (abs(brightness - lastbrightness) >= brightThreshold) { if (_transportConfig.parentNodeId == 0) { if (send(brightMsg.set(brightness, 0))) { err_delivery_beat = 0; if (flag_nogateway_mode == 1) { flag_nogateway_mode = 0; CORE_DEBUG(PSTR("MyS: NORMAL GATEWAY MODE\n")); err_delivery_beat = 0; } lastbrightness = brightness; if (wpm_enable == 1) { Wpm = GetWpm(); wait(100); send(wpmMsg.set(Wpm, 0)); } wait(50); blinky(2, 2, BLUE_LED); } else { _transportSM.failedUplinkTransmissions = 0; if (err_delivery_beat < 5) { err_delivery_beat++; } if (err_delivery_beat == 4) { if (flag_nogateway_mode == 0) { gateway_fail(); CORE_DEBUG(PSTR("MyS: LOST GATEWAY MODE\n")); } } } } if (_transportConfig.parentNodeId > 0) { send(brightMsg.set(brightness, 0), 1); wait(2500, C_SET, V_LEVEL); if (Ack_TL == 1) { Ack_TL = 0; err_delivery_beat = 0; if (flag_nogateway_mode == 1) { flag_nogateway_mode = 0; CORE_DEBUG(PSTR("MyS: NORMAL GATEWAY MODE\n")); err_delivery_beat = 0; } lastbrightness = brightness; if (wpm_enable == 1) { Wpm = GetWpm(); wait(100); send(wpmMsg.set(Wpm, 0)); } wait(50); blinky(2, 2, BLUE_LED); } else { _transportSM.failedUplinkTransmissions = 0; if (err_delivery_beat < 5) { err_delivery_beat++; } if (err_delivery_beat == 4) { if (flag_nogateway_mode == 0) { gateway_fail(); CORE_DEBUG(PSTR("MyS: LOST GATEWAY MODE\n")); } } } } } } else { send(brightMsg.set(brightness, 0)); lastbrightness = brightness; if (wpm_enable == 1) { Wpm = GetWpm(); wait(100); send(wpmMsg.set(Wpm, 0)); } wait(50); blinky(2, 2, BLUE_LED); } } void interrupt_Init() { //*** //SET //NRF_GPIO_PIN_NOPULL //NRF_GPIO_PIN_PULLUP //NRF_GPIO_PIN_PULLDOWN //*** nrf_gpio_cfg_input(PIN_BUTTON1, NRF_GPIO_PIN_PULLUP); nrf_gpio_cfg_input(AXEL_INT1, NRF_GPIO_PIN_NOPULL); APP_GPIOTE_INIT(APP_GPIOTE_MAX_USERS); PIN_BUTTON1_MASK = 1 << PIN_BUTTON1; AXEL_INT1_MASK = 1 << AXEL_INT1; // app_gpiote_user_register(p_user_id, pins_low_to_high_mask, pins_high_to_low_mask, event_handler) app_gpiote_user_register(&m_gpiote_user_id, AXEL_INT1_MASK, PIN_BUTTON1_MASK, gpiote_event_handler); app_gpiote_user_enable(m_gpiote_user_id); axelInt1Status = 0; buttInt1Status = 0; } void gpiote_event_handler(uint32_t event_pins_low_to_high, uint32_t event_pins_high_to_low) { MY_HW_RTC->CC[0] = (MY_HW_RTC->COUNTER + 2); if (PIN_BUTTON1_MASK & event_pins_high_to_low) { if ((buttInt1Status == 0) && (axelInt1Status == 0)) { buttInt1Status = PIN_BUTTON1; } } if (flag_nogateway_mode == 0) { if (configMode == 0) { if (AXEL_INT1_MASK & event_pins_low_to_high) { if ((axelInt1Status == 0) && (buttInt1Status == 0)) { axelInt1Status = AXEL_INT1; } } } } /*** if ((PIN_BUTTON_MASK & event_pins_low_to_high) || (PIN_BUTTON1_MASK & event_pins_high_to_low)) ***/ } void device_Conf() { conf_vibro_set = loadState(230); if ((conf_vibro_set > 5) || (conf_vibro_set == 0)) { conf_vibro_set = 1; saveState(230, conf_vibro_set); } wpm_enable = loadState(240); if (wpm_enable > 1) { wpm_enable = 0; saveState(240, wpm_enable); } interval_reading_lux = loadState(220); if (interval_reading_lux > 60) { interval_reading_lux = 60; saveState(230, interval_reading_lux); } else if (interval_reading_lux < 1) { interval_reading_lux = 1; saveState(230, interval_reading_lux); } SLEEP_TIME = SLEEP_TIME_TEMP * interval_reading_lux; C_BATT_TIME = BATT_TIME / SLEEP_TIME; } void sendBatteryStatus(bool start) { sleep(5000); wait(200); batteryVoltage = hwCPUVoltage(); wait(10); batt_cap = battery_level_in_percent(batteryVoltage); if (start == 1) { if (batt_cap < old_batt_cap) { sendBatteryLevel(battery_level_in_percent(batteryVoltage), 1); wait(2500, C_INTERNAL, I_BATTERY_LEVEL); old_batt_cap = batt_cap; } } else { sendBatteryLevel(battery_level_in_percent(batteryVoltage), 1); wait(2500, C_INTERNAL, I_BATTERY_LEVEL); } linkQuality = calculationRxQuality(); if (linkQuality != old_linkQuality) { wait(10); sendSignalStrength(linkQuality); wait(50); old_linkQuality = linkQuality; } } bool sendSignalStrength(const int16_t level, const bool ack) { return _sendRoute(build(_msgTmp, GATEWAY_ADDRESS, SIGNAL_Q_ID, C_SET, V_VAR1, ack).set(level)); } int16_t calculationRxQuality() { int16_t nRFRSSI_temp = transportGetReceivingRSSI(); int16_t nRFRSSI = map(nRFRSSI_temp, -85, -40, 0, 100); if (nRFRSSI < 0) { nRFRSSI = 0; } if (nRFRSSI > 100) { nRFRSSI = 100; } return nRFRSSI; } void happy_init() { //hwWriteConfig(EEPROM_NODE_ID_ADDRESS, 255); // ******************** checking the node config reset ************************* if (hwReadConfig(EEPROM_NODE_ID_ADDRESS) == 0) { hwWriteConfig(EEPROM_NODE_ID_ADDRESS, 255); } if (loadState(100) == 0) { saveState(100, 255); } CORE_DEBUG(PSTR("EEPROM NODE ID: %d\n"), hwReadConfig(EEPROM_NODE_ID_ADDRESS)); CORE_DEBUG(PSTR("USER MEMORY SECTOR NODE ID: %d\n"), loadState(100)); if (hwReadConfig(EEPROM_NODE_ID_ADDRESS) == 255) { mtwr = 0; } else { mtwr = 10000; no_present(); } CORE_DEBUG(PSTR("MY_TRANSPORT_WAIT_MS: %d\n"), mtwr); } void new_device() { hwWriteConfig(EEPROM_NODE_ID_ADDRESS, 255); saveState(100, 255); wdt_enable(WDTO_15MS); } void config_Happy_node() { if (mtwr == 0) { myid = getNodeId(); saveState(100, myid); mypar = _transportConfig.parentNodeId; old_mypar = mypar; master_id = 0; // *************************** master slave mode is not initialized in this example, ..stub ******************************* saveState(101, mypar); saveState(102, _transportConfig.distanceGW); } if (mtwr != 0) { myid = getNodeId(); if (myid != loadState(100)) { saveState(100, myid); } if (isTransportReady() == true) { mypar = _transportConfig.parentNodeId; master_id = 0; // *************************** master slave mode is not initialized in this example, ..stub ******************************* if (mypar != loadState(101)) { saveState(101, mypar); } if (_transportConfig.distanceGW != loadState(102)) { saveState(102, _transportConfig.distanceGW); } present_only_parent(); } if (isTransportReady() == false) { no_present(); flag_fcount = 1; err_delivery_beat = 5; _transportConfig.nodeId = myid; _transportConfig.parentNodeId = loadState(101); _transportConfig.distanceGW = loadState(102); mypar = _transportConfig.parentNodeId; happy_node_mode(); gateway_fail(); } } } void no_present() { _coreConfig.presentationSent = true; _coreConfig.nodeRegistered = true; } void happy_node_mode() { _transportSM.findingParentNode = false; _transportSM.transportActive = true; _transportSM.uplinkOk = true; _transportSM.pingActive = false; transportSwitchSM(stReady); _transportSM.failureCounter = 0; } void gateway_fail() { flag_nogateway_mode = 1; flag_update_transport_param = 0; SLEEP_TIME_W = SLEEP_TIME / 2; lis2->Disable_Wake_Up_Detection(); } void find_parent_process() { flag_update_transport_param = 1; flag_find_parent_process = 0; CORE_DEBUG(PSTR("MyS: STANDART TRANSPORT MODE IS RESTORED\n")); err_delivery_beat = 0; lis2->Enable_Wake_Up_Detection(); } void update_Happy_transport() { CORE_DEBUG(PSTR("MyS: UPDATE TRANSPORT CONFIGURATION\n")); mypar = _transportConfig.parentNodeId; master_id = 0; // *************************** master slave mode is not initialized in this example, ..stub ******************************* if (mypar != loadState(101)) { saveState(101, mypar); } if (_transportConfig.distanceGW != loadState(102)) { saveState(102, _transportConfig.distanceGW); } present_only_parent(); wait(50); nosleep = 0; flag_update_transport_param = 0; } void present_only_parent() { if (old_mypar != mypar) { CORE_DEBUG(PSTR("MyS: SEND LITTLE PRESENT:) WITH PARENT ID\n")); if (_sendRoute(build(_msgTmp, 0, NODE_SENSOR_ID, C_INTERNAL, 6).set(mypar))) { flag_sendRoute_parent = 0; old_mypar = mypar; } else { flag_sendRoute_parent = 1; } } } void check_parent() { _transportSM.findingParentNode = true; CORE_DEBUG(PSTR("MyS: SEND FIND PARENT REQUEST, WAIT RESPONSE\n")); _sendRoute(build(_msg, 255, NODE_SENSOR_ID, C_INTERNAL, 7).set("")); wait(1500, C_INTERNAL, 8); if (_msg.sensor == 255) { if (mGetCommand(_msg) == 3) { if (_msg.type == 8) { Ack_FP = 1; CORE_DEBUG(PSTR("MyS: PARENT RESPONSE FOUND\n")); } } } if (Ack_FP == 1) { CORE_DEBUG(PSTR("MyS: FIND PARENT PROCESS\n")); Ack_FP = 0; transportSwitchSM(stParent); flag_nogateway_mode = 0; flag_find_parent_process = 1; SLEEP_TIME_W = SLEEP_TIME; problem_mode_count = 0; } else { _transportSM.findingParentNode = false; CORE_DEBUG(PSTR("MyS: PARENT RESPONSE NOT FOUND\n")); _transportSM.failedUplinkTransmissions = 0; nosleep = 0; if (problem_mode_count < 24) { CORE_DEBUG(PSTR("PROBLEM MODE COUNTER: %d\n"), problem_mode_count); problem_mode_count++; SLEEP_TIME_W = SLEEP_TIME / 100 * 120; } else if (problem_mode_count == 24) { SLEEP_TIME_W = SLEEP_TIME * 30; CORE_DEBUG(PSTR("PROBLEM MODE COUNTER: %d\n"), problem_mode_count); } } } void receive(const MyMessage & message) { if (message.sensor == ENABLE_WPM_SENS_CHILD_ID) { if (message.type == V_VAR1) { if (mGetCommand(message) == C_SET) { if (message.isEcho()) { Ack_TL = 1; } else { wpm_enable = message.getBool(); saveState(240, wpm_enable); wait(10); send(conf_wpmMsg.set(wpm_enable)); wait(50); blinky(3, 3, GREEN_LED); configMode = 0; nosleep = 0; button_flag = 0; buttInt1Status = 0; } } } } if (message.sensor == LEVEL_SENSIV_V_SENS_CHILD_ID) { if (message.type == V_VAR1) { if (mGetCommand(message) == C_SET) { if (message.isEcho()) { Ack_TL = 1; } else { conf_vibro_set = message.getByte(); vibro_Init(); saveState(230, conf_vibro_set); wait(10); send(conf_vsensMsg.set(conf_vibro_set)); wait(50); blinky(3, 3, GREEN_LED); configMode = 0; nosleep = 0; button_flag = 0; buttInt1Status = 0; } } } } if (message.sensor == INTERVAL_R_LUX_CHILD_ID) { if (message.type == V_VAR1) { if (mGetCommand(message) == C_SET) { if (message.isEcho()) { Ack_TL = 1; } else { interval_reading_lux = message.getByte(); SLEEP_TIME = SLEEP_TIME_TEMP * interval_reading_lux; C_BATT_TIME = BATT_TIME / SLEEP_TIME; saveState(220, interval_reading_lux); wait(10); send(conf_interv_rluxMsg.set(interval_reading_lux)); wait(50); blinky(3, 3, GREEN_LED); configMode = 0; nosleep = 0; button_flag = 0; buttInt1Status = 0; } } } } if (message.sensor == LUX_SENS_CHILD_ID) { if (message.type == V_LEVEL) { if (mGetCommand(message) == C_SET) { if (message.isEcho()) { Ack_TL = 1; } } } } if (message.sensor == V_SENS_CHILD_ID) { if (message.type == V_TRIPPED) { if (mGetCommand(message) == C_SET) { if (message.isEcho()) { Ack_TL = 1; } } } } if (mGetCommand(message) == 0) { PRESENT_ACK = 1; CORE_DEBUG(PSTR("MyS: !!!ACK OF THE PRESENTATION IN THE FUNCTION RECEIVE RECEIVED!!!\n")); } } 


Projet github (bibliothèques supplémentaires, sources de projet, schémas, gerberas, nomenclature):
github.com/smartboxchannel/EFEKTA-LIS2DW12-MAX44009-E73C

Caractéristiques du capteur:

  • Plage de tension de fonctionnement 2-3 V,
  • 6,7 μA en mode veille,
  • 8mA en mode réseau,
  • E73-2G4M08S1C nRF52840,
  • Capteur de lumière ambiante MAX44009,
  • Accéléromètre MEMS 3 axes LIS2DW12 \ LIS2DH12,
  • LED RGB
  • Utilisateur botton,
  • Port de programmation SWD + série.

Ci-dessous est un exemple du fonctionnement du capteur dans le système Majordomo UD, il fonctionnera naturellement dans tout UD dans lequel le support du protocole MySensors est ajouté, et c'est presque tout connu.





Vidéo avec l'intérieur de la carte, un exemple de capteur, un exemple de paramètres


Photo du capteur






Je vais probablement terminer là-dessus.

Un endroit où vous êtes toujours heureux d'aider tous ceux qui veulent faire connaissance avec MYSENSORS (installer des cartes, travailler avec des microcontrôleurs nRF5 dans l'environnement Arduino IDE, des conseils pour travailler avec le protocole mysensors, discuter de projets - chat télégramme @mysensors_rus

Bonne chance et bonne chance dans vos projets!

PS / Partager avec des plans immédiats, bientôt un article sera publié sur une version mise à jour de mon capteur de température et d'humidité avec un écran à encre électronique, enfin maintenant c'est un appareil fini et non un module, il y aura aussi un article sur un capteur ouvert / fermé avec un interrupteur à lames, un accéléromètre et un capteur champ magnétique et encodeur sur nRF52811 Photos et vidéo ci-dessous dans le spoiler.

Spoiler




















Source: https://habr.com/ru/post/fr478960/


All Articles