a savoir aussi...
pour rentrer dans sa place ...que ce soit un 40 pieds ou un 70 pieds (en gros 13m et 20 m) le deplacement est de l'ordre du metre pour 30 seconde voir une minute...
Version imprimable
a savoir aussi...
pour rentrer dans sa place ...que ce soit un 40 pieds ou un 70 pieds (en gros 13m et 20 m) le deplacement est de l'ordre du metre pour 30 seconde voir une minute...
comme ca vous comprenez....
https://www.youtube.com/watch?v=y8wS9XnXvJQ
wow - c'est digne du créneau parisien :)
En fait je me dis que les vendeurs de ces bateaux ne veulent pas prendre de risque avec des radars de recul qui ne fonctionnerait pas vu les risques de procès si un beep n'a pas marché.. ça doit chiffrer vite si on enfonce un autre bateau!
ce n'est pas ma cible sur le genre de bateau qu'il y a sur la video.... (il y a 10 membres d'equipage et les mec on l'habitude) ca parait comme ca mais ce sont des jouet a manoeuvrer....
non c'est pour des bateau plus raisonnable ..jusqu'a 20 m
il y a beaucoup de nouveau riche qui achète un bateau mais qui n'y connaisse rien... et ils ce font peur.... c'est ceux la que je vise
on essaie?
Le capteur fonctionne par mesure du temps pour recevoir la réflexion de l'onde émise sur la cible.
On réfléchit toujours mieux avec un petit dessin:
si on n'est pas sur l'horizontale (et idéalement au bout du bateau) la zone "éclairée" ne sera pas plane (on aura de l'eau et le quai et éventuellement derrière le quai).
Ici la zone rouge "éclairée" va rebondir dans tous les sens, une partie sera absorbée aussi. Je pense qu'on va lire n'importe quoi (des rebonds qui reviendraient vers le capteur)
Pièce jointe 591596
faites des tests avec le capteur faisant un angle avec le sol et regardez ce que vous obtenez
Bonjour Jay,
j'ai conscience de ce phenomene de "ricocher"
le probleme c'est qu'on sera jamais pile dans l'axe sur un bateau...
en regle general les quai sont plus bas que le pont principal du bateau et parfois, comme expliqué precedement, les quai sont sur pilotis...
trouver un autre capteur?
combiner deux ou 3 meme capteur?
Ok
Globalement si on n’a pas une surface orthogonale au rayon laser ou ultra sons - ce type de capteur ne fonctionnera pas. Le cas du quai en bois est aussi difficile à traiter...
Je pense que c’est plus complexe qu’une première approche pourrait laisser penser et que ça explique le fait que les constructeurs de bateaux n’en mettent pas et que sur les gros il y a des caméras et ils laissent aux humains le soin de piloter...
Je sèche sur une approche réaliste qui fonctionnerait avec un arduino. On tombe plus dans le cas du logiciel pour voiture a conduite autonome qui doit détecter des situations connues sur la base d’une tripotée de capteurs lidars en matrice et autres caméras. Il faut une puissance de calcul importante, beaucoup d’éléments câblés et un entraînement des modèles IA en cas réel... des années de R&D...
Bonjour Jay
Hummm... j'aimerais tester quand même...
pouvez vous me sortir un code pour l'ultrason avec les beep comme je vous avez indiqué? (par palier de 1 m)
il faudrait aussi la possibilité de faire comme un decalage a l'aide de dip switch ou d'un encodeur rotatif
ex: le capteur est a 1 m du cul du bateau on decale de 1 metre les beep
si il est a 2 metre pareil on decale
au moins 4 positions
une sortie balayage tilt que je regleré dans le code
et une sortie pour le HC12...
si vous avez un peu de temps ce serais cool...
j'ai trouvé un code avec un HC-SR04...
on pourrais essayer de mettre 2 Maxbotix a la place du HC-SR04
un envois Trigger... retour echo on a peux etre plus de perf...
dans le code il y a le Tilt aussi et sortie buzzer
si ca peux vous simplifier la vie... enfin ci vous voulez toujours m'aider...
je ne sais toujour pas comment on poste les codes....
là je suis en vadrouille pour quelques jours sans mon matos...
passez toujours le lien vers le code que vous avez trouvé
Mais avant de coder quoi que ce soit, faites des essais avec le capteur faisant un angle plus ou moins important avec le sol / quai et essayez de voir ce que vous lisez. Je pense que ça va sortir n'importe quoi...
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164 #include <Servo.h> #include <LiquidCrystal.h> Servo myservo; LiquidCrystal lcd(7, 6, 5, 4, 3, 2); // Creates an LCD object. Parameters: (rs, enable, d4, d5, d6, d7) int pos = 0; // la position initiale du servo moteur const int trigPin = 9; const int echoPin = 10; const int moteur = 11; const int buzzer = 12; const int ledPin1 = 14; const int ledPin2 = 15; float distanceCm, DistanceSec,duration; void setup() { myservo.attach(moteur); // attache le Servo moteur a la pin numéro 11 lcd.begin(16,2); // Initialiser l'interface de Lcd avec leurs Dimensions pinMode(trigPin, OUTPUT); pinMode(echoPin, INPUT); pinMode(buzzer, OUTPUT); pinMode(ledPin1, OUTPUT); pinMode(ledPin2, OUTPUT); DistanceSec=20; } void loop() { for (pos = 0; pos <= 180; pos += 1) { // aller de 0 a 180 degée // in steps of 1 degree myservo.write(pos); // Programmer le Servo pour aller a la position (pos) digitalWrite(trigPin, LOW); delayMicroseconds(2); digitalWrite(trigPin, HIGH); //envoyer une impulsion de 10 micro seconds delayMicroseconds(10); digitalWrite(trigPin, LOW); duration = pulseIn(echoPin, HIGH); distanceCm= duration*0.034/2; if (distanceCm <= DistanceSec) { if(distanceCm <= DistanceSec/2) { tone(buzzer, 10); // Send 1KHz sound signal... digitalWrite(ledPin1, LOW); digitalWrite(ledPin2, HIGH); delay(700); noTone(buzzer); // Stop sound... lcd.setCursor(0,0); // positionner le cursor a 0,0 lcd.print("Distance: "); // Printe "Distance" sur LCD lcd.print(distanceCm); // Printe la valeur Obtenue sur LCD lcd.print(" cm "); // Printe l'unité sur LCD delay(10); lcd.setCursor(0,1); lcd.print("Angle : "); lcd.print(pos); lcd.print(" deg "); delay(2000); } else { digitalWrite(buzzer, HIGH); digitalWrite(ledPin2, LOW); digitalWrite(ledPin1, HIGH); delay(100); digitalWrite(buzzer, LOW); lcd.setCursor(0,0); // positionner le cursor a 0,0 lcd.print("Distance: "); // Printe "Distance" sur LCD lcd.print(distanceCm); // Printe la valeur Obtenue sur LCD lcd.print(" cm "); // Printe l'unité sur LCD delay(10); lcd.setCursor(0,1); lcd.print("Angle : "); lcd.print(pos); lcd.print(" deg "); delay(2000); } } else{ digitalWrite(buzzer, LOW); digitalWrite(ledPin1, LOW); digitalWrite(ledPin2, LOW); } lcd.setCursor(0,0); // positionner le cursor a 0,0 lcd.print("Distance: "); // Printe "Distance" sur LCD lcd.print(distanceCm); // Printe la valeur Obtenue sur LCD lcd.print(" cm "); // Printe l'unité sur LCD delay(10); lcd.setCursor(0,1); lcd.print("Angle : "); lcd.print(pos); lcd.print(" deg "); delay(80); //attendre 100ms pour que le servo cherche sa position } for (pos = 180; pos >= 0; pos -= 1) { // myservo.write(pos); // digitalWrite(trigPin, LOW); delayMicroseconds(2); digitalWrite(trigPin, HIGH); delayMicroseconds(10); digitalWrite(trigPin, LOW); duration = pulseIn(echoPin, HIGH); distanceCm= duration*0.034/2; if (distanceCm <= DistanceSec){ if(distanceCm <= DistanceSec/2) { tone(buzzer, 10); // Send 1KHz sound signal... digitalWrite(ledPin1, LOW); digitalWrite(ledPin2, HIGH); delay(700); noTone(buzzer); // Stop sound... lcd.setCursor(0,0); // positionner le cursor a 0,0 lcd.print("Distance: "); // Printe "Distance" sur LCD lcd.print(distanceCm); // Printe la valeur Obtenue sur LCD lcd.print(" cm "); // Printe l'unité sur LCD delay(10); lcd.setCursor(0,1); lcd.print("Angle : "); lcd.print(pos); lcd.print(" deg "); delay(2000); } else { digitalWrite(buzzer, HIGH); digitalWrite(ledPin2, LOW); digitalWrite(ledPin1, HIGH); delay(100); digitalWrite(buzzer, LOW); lcd.setCursor(0,0); // positionner le cursor a 0,0 lcd.print("Distance: "); // Printe "Distance" sur LCD lcd.print(distanceCm); // Printe la valeur Obtenue sur LCD lcd.print(" cm "); // Printe l'unité sur LCD delay(10); lcd.setCursor(0,1); lcd.print("Angle : "); lcd.print(pos); lcd.print(" deg "); delay(2000); } } else{ digitalWrite(buzzer, LOW); digitalWrite(ledPin1, LOW); digitalWrite(ledPin2, LOW); } lcd.setCursor(0,0); // lcd.print("Distance: "); // lcd.print(distanceCm); // lcd.print(" cm "); delay(10); lcd.setCursor(0,1); lcd.print("Angle : "); lcd.print(pos); lcd.print(" deg "); delay(80); } }
Je n'ai pas mes Arduinos mais j'ai mes outils de bricolage :)
J'ai fait un petit test avec un mètre laser avec point rouge (donc zone très précise): Même en étant incliné on arrive à avoir une lecture de distance cohérente - donc c'est peut-être jouable.
Le code que vous postez fait un "sweep" (balayage) entre 0 et 180° puis dans l'autre sens.
donc il suffit régler le 0 et 180 dans les boucles et mettre autre chose si on veut se balader entre 2 valeurs.Code:
1
2
3 for (pos = 0; pos <= 180; pos += 1) { // aller de 0 a 180 degrés ... for (pos = 180; pos >= 0; pos -= 1) { //
Pour chaque angle il lit une distance, effectue l'affichage puis fait un beep s'il le faut (L'auteur du code aurait pu faire une fonction de lecture et affichage de distance mais l'auteur à préféré dupliquer le code)
Sur une Nano il se peut que la bibliothèque Servo soit en conflit avec la fonction tone() - faudra que je teste quand je peux.
je vais voir si je peux vous taper un bout de code à tester
ahhhhh! ouf j'ai eu peur que vous me laissiez tomber....!
j'ai synthétisé un peu notre discussion....
il faut que le client éventuel parte dans l'idée que les infos données par le systeme ne sont QU INDICATIVE....
il faudrait 2 code....
un avec tilt et un sans tilt
on garde les different paliers: beep a partir de 8 m, 6, 5, 4, 3 ,2 beep continue (chaque palier une frequence de beep differente)
on redistribue l'infos beep pour un ou 2 endroit different (poste pilote )
et puisqu'on est dans l'infos pour le pilote.... j'ai commandé des petit ecran "oled" pour arduino, je fait un adhesif transparent qui represente le cul du bateau en haut et en bas le quai, entre les 2 on affiche la distance....
j'ai regardé les doc Maxsonar, il dise qu'on peux les utiliser en duo... est ce que ca apporterait un avantage...
ah oui ...Bonne Vadrouille...
le projet est fun - autant j'aime explorer et prototyper pour m'amuser autant j'ai passé l'age de gérer les soucis d'industrialisation :) (la partie chiante)
Je pense que si vous voulez en faire un produit et le commercialiser / maintenir, il serait pas mal de trouver un freelance un peu costaud qui puisse s'impliquer à fond (et comme vous l'avez peut-être compris je n'ai pas besoin de rémunération supplémentaire)
pour revenir au projet:
- ce que vous appelez tilt, c'est une version avec le servo qui balaye une zone ?
- un des soucis de la notification de beep distribuée c'est de choisir la bonne radio: le module HC12 utilise un port série mais votre XL-MaxSonar-WRC1 aussi si on veut une lecture précise. il n'y a qu'un seul port Série hardware sur les Nano. si on le prend pour la mesure de distance par exemple, ça veut dire qu'il faudra débrancher le module pendant le téléchargement de code (pénible mais jouable).
- la consommation électrique des modules distants restera élevée (module radio + écran + audio + arduino). il faudra trouver un moyen de les allumer en même temps que le radar
- Quand vous ditesvous voulez dire que ta tonalité du son entendu est à des fréquences différentes suivant la distance (plus ou moins aigu) ou vous voulez dire (comme j'avais précédemment et comme c'est fait sur les voitures) que les beeps sont plus ou moins rapprochés suivant la distance restante?Citation:
on garde les different paliers: beep a partir de 8 m, 6, 5, 4, 3 ,2 beep continue (chaque palier une fréquence de beep différentes)
j'ai deja un mec qui s'implique enormement et qui a beaucoup de savoir....
la comercialisation c'est autre chose....
pour moi, ce qui m'interesse c'est le concept, qu'on peux faire a deux, moi je gere l'electronique et l'integration, en programation je suis une chevre!
les beeps: pour chaqque palier les beep on a la meme tonalitee et la meme amplitude mais il sont plus rapproché a l'approche des 2 metres
en gros de 8 a 7 metre ca beep a une frequence
de 7 a 6 metre ca beep un peux plus vite
de 6 a 5 metre idem
pendant le metre on beep de la meme maniere
pour la transmition on met ce que vos sentez le mieux....
quand a l'affichage je pense que c'est un plus....
Salut
Je rentre aujourd’hui et je repars prendre l’air (ca sent le confinement autant en profiter un peu avant !) la semaine prochaine - je vais essayer de regarder ce qui est faisable ce week end
Salut Jay,
ah oui vous avez raison....! nous ici sur la cote d'azur ca sent pas bon du tout....
super si vous pouvez faire une truc pour que je puisse faire des essais...
en affichage j'ai recu ca:https://www.amazon.fr/gp/product/B07...?ie=UTF8&psc=1
et ca:https://www.amazon.fr/gp/product/B08...?ie=UTF8&psc=1
quand a l'arduino... si on peux partir sur un Nano coté radar et des micro pro cotés buzzer....
Pour Vous Montrer....
le GPS....Pièce jointe 592044
Joli PCB !
Bon j'ai bossé un peu ce matin sur une première version à tester avec le XL-MaxSonar-WRC1 et des "beepers" distribués
- Le "Master" embarque le sonar, un écran LCD, un encodeur rotatif, un piezo et un HC12
- Un esclave embarque un piezo et un HC12
J'ai mis un résistance de 150Ω*sur la pin de commande des piezos par précaution (la première charge du piezo peut tirer pas mal de courant, au dela de ce qu'une pin Arduino supporte).
Master:
Câblage:
Alimentation:Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 0 Rx Radar TX 1 Tx Radar Rx 2 Encoder DT 3 Encoder CLK 4 Encoder SW 5 6 Piezo + (through 150Ω current limiting resistor) 7 HC12 Set 8 SRx HC12 Tx (AltSoftSerial imposed) 9 STx HC12 Rx (AltSoftSerial imposed) 10 11 12 13 Led A0 A1 A2 A3 A4 SCA LCD SCA A5 SCL LCD SCL
Pour le moment l'encodeur rotatif ne sert à rien. Dans le futur ce serait pour choisir le canal de communication et la puissanceCode:
1
2
3
4 HC12 : 5V, GND LCD : GND, 5V Encoder : GND, 5V Arduino : USB, 7-20V unregulated on pin 30 (Vin), 5V regulated on pin 27 (5V)
Il faut installer les Bibliothèques suivantes:
Encoder https://www.pjrc.com/teensy/td_libs_Encoder.html
simpleBouton http://forum.arduino.cc/index.php?topic=375232.0 (@bricoleau)
LiquidCrystal_I2C https://github.com/fdebrabander/Ardu...al-I2C-library
AltSoftSerial https://github.com/PaulStoffregen/AltSoftSerial
TimerFreeTone https://bitbucket.org/teckel12/arduino-timer-free-tone
Le code:
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382 // Author: Jay M for Arduino forums. // Standard MIT License applies. https://opensource.org/licenses/mit-license.php /* *** ARDUINO PIN MAPPING *** 0 Rx Radar TX 1 Tx Radar Rx 2 Encoder DT 3 Encoder CLK 4 Encoder SW 5 6 Piezo + (through 150Ω current limiting resistor) 7 HC12 Set 8 SRx HC12 Tx (AltSoftSerial imposed) 9 STx HC12 Rx (AltSoftSerial imposed) 10 11 12 13 Led A0 A1 A2 A3 A4 SCA LCD SCA A5 SCL LCD SCL *** POWER *** HC12 : 5V, GND LCD : GND, 5V Encoder : GND, 5V Arduino : USB, 7-20V unregulated on pin 30 (Vin), 5V regulated on pin 27 (5V) *** WIRING *** Piezo : arduino --- 150Ω --- Piezo+ () Piezo- --- GND (to limit this initial inrush current) *** LIBRARIES *** future: Using a Servo, Piezo and a Software Serial connection requires care as they depend on scarce Timers or Pin Change Interrupts The choice below will work. AltSoftSerial imposes pins for Rx/Tx. TimerFreeTone is blocking but does not conflict with ServoTimer2 Encoder https://www.pjrc.com/teensy/td_libs_Encoder.html simpleBouton http://forum.arduino.cc/index.php?topic=375232.0 (@bricoleau) LiquidCrystal_I2C https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library AltSoftSerial https://github.com/PaulStoffregen/AltSoftSerial TimerFreeTone https://bitbucket.org/teckel12/arduino-timer-free-tone */ #include <Encoder.h> // https://www.pjrc.com/teensy/td_libs_Encoder.html #include <simpleBouton.h> // http://forum.arduino.cc/index.php?topic=375232.0 (@bricoleau) #include <LiquidCrystal_I2C.h> // https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library #include <AltSoftSerial.h> // https://github.com/PaulStoffregen/AltSoftSerial #include <TimerFreeTone.h> // https://bitbucket.org/teckel12/arduino-timer-free-tone #include <EEPROM.h> const uint32_t hc12BaudRates[] = {9600, 4800, 1200, 2400, 19200, 38400, 57600, 115200}; // 9600 is the default so testing first const uint8_t nbBaudRates = sizeof hc12BaudRates / sizeof hc12BaudRates[0]; // set channels 6 bands apart to avoid interferences with nearbye devices const char* hc12Channels[] = {"C003", "C009", "C015", "C021", "C027", "C033", "C039", "C045", "C051", "C057", "C063", "C069", "C075", "C081", "C087", "C093"}; const uint8_t nbChannels = sizeof hc12Channels / sizeof hc12Channels[0]; // 16 to be selected through DIL DIP SWITCH bool HC12Ready = false; // ****** LCD ****** LiquidCrystal_I2C lcd(0x3F, 16, 2); // address, #cols, #lines. NB other LCD may use 0x27 // ****** EEPROM ARCHIVE ****** struct __attribute__ ((packed)) _paramS { uint8_t fuMode; // 1 to 4 uint8_t powerMode; // 1 to 8 uint8_t channelIndex; // 0 to 15 uint8_t baudRateIndex; // 0 to 7, index in hc12BaudRates array } hc12Parameters; const uint32_t keyword = 0xDEADBEEF; const uint16_t keywordAddress = 0x00; const uint16_t paramAddress = keywordAddress + sizeof(keyword); void lcdPrintBin(uint8_t b, int8_t startPos = 7) { for (int8_t p = startPos; p >= 0; --p) lcd.write(bitRead(b, p) ? '1' : '0'); } void printDefaults() { lcd.clear(); lcd.setCursor(0, 0); lcd.print(F("SW: -.")); lcdPrintBin(hc12Parameters.powerMode, 2); // 3 bits for POWER lcd.write('.'); lcdPrintBin(hc12Parameters.channelIndex, 3); // 4 bits for } void saveDefaults() { EEPROM.put(keywordAddress, keyword); EEPROM.put(paramAddress, hc12Parameters); } void getDefaults(bool forceInitialize = false) { uint32_t tmpKey; EEPROM.get(keywordAddress, tmpKey); if (!forceInitialize && (tmpKey == keyword)) { bool valid = true; EEPROM.get(paramAddress, hc12Parameters); // EEPROM was already initialized OK to read // check for coherence if ((hc12Parameters.fuMode == 0) || (hc12Parameters.fuMode > 4)) { hc12Parameters.fuMode = 2; // FU2 valid = false; } if ((hc12Parameters.powerMode == 0) || (hc12Parameters.powerMode > 8)) { hc12Parameters.powerMode = 4; // P4 valid = false; } if (hc12Parameters.channelIndex > nbChannels) { hc12Parameters.channelIndex = 0; // C003 valid = false; } if (hc12Parameters.fuMode >= nbBaudRates) { hc12Parameters.baudRateIndex = 1; // 4800 bauds valid = false; } if (!valid) saveDefaults(); } else { // First run on this arduino, memory was never initialized. so establish default values hc12Parameters.fuMode = 2; // AT+FU2 --> 80µA idle hc12Parameters.powerMode = 4; // AT+P4 --> 8dBm, 6.3mW hc12Parameters.channelIndex = 0; // AT+C003 hc12Parameters.baudRateIndex = 1; // AT+B4800 --> 4800 bauds saveDefaults(); } } // ****** AUDIO ****** const uint8_t buzzPin = 6; // Piezo + through a current limiting resistor (150Ω) const uint16_t buzFreq = 1000; // frequency used during distance beeping // Rotary encoder const uint8_t encoderDTPin = 2; // Encoder DT const uint8_t encoderCLKPin = 3; // Encoder CLK const uint8_t encoderSwitchPin = 4; // Encoder SW long oldRotaryEncoderPosition; Encoder rotaryEncoder(encoderDTPin, encoderCLKPin); simpleBouton encoderSwitch(encoderSwitchPin); // ****** XL-MaxSonar-WRC1 ****** #define maxSonar Serial const uint32_t sonarBaudRate = 9600; const uint16_t minDistanceAlert_cm = 50; // in cm. continuous beep if distance less than 50 cm const uint16_t maxDistanceAlert_cm = 900; // in cm. Start beeping if distance less than 9 m uint16_t distance = 2 * maxDistanceAlert_cm; // large value to avoid beeping at first const char minDistanceBeepCode = '0'; // continuous beep const char maxDistanceBeepCode = '9'; // no beep const uint8_t sleepPillCode = 'S'; // 'S' for sleep const uint8_t wakeUpPillCode = 'W'; // 'W' for wakeup (will be lost but triggers pin change interrupt) char distanceCode = maxDistanceBeepCode; // very crude XL-MaxSonar-WRC1 reading interface based on https://www.robotshop.com/media/files/PDF/datasheet-mb7060.pdf // Serial @ 9600 8N1 (0 - Vcc) // Frame format: Rxxx<CR> where xxx is the distance in cm, start marker is 'R', end marker is a carriage return (ASCII 13) enum : byte {STARTMARKER, PAYLOAD, ENDMARKER} parserState = STARTMARKER; bool acquisition() { static uint16_t payloadDistance; static uint8_t payloadByteCount; bool acquired = false; int rec = maxSonar.read(); if (rec != -1) { uint16_t byteReceived = rec & 0xFF; switch (parserState) { case STARTMARKER: if (byteReceived == 'R') { payloadByteCount = 0; payloadDistance = 0; parserState = PAYLOAD; } break; case PAYLOAD: if ((byteReceived >= '0') && (byteReceived <= '9')) { payloadDistance = 10 * payloadDistance + (byteReceived - '0'); if (++payloadByteCount >= 3) parserState = ENDMARKER; } else if (byteReceived == 'R') { // handle double 'R' payloadByteCount = 0; payloadDistance = 0; } else parserState = STARTMARKER; break; case ENDMARKER: if (byteReceived == '\r') { acquired = true; distance = payloadDistance; parserState = STARTMARKER; } else if (byteReceived == 'R') { payloadByteCount = 0; payloadDistance = 0; parserState = PAYLOAD; } else parserState = STARTMARKER; break; } } return acquired; } // ****** HC12 ****** // https://opencircuit.shop/resources/file/0f8d974f31fd813604c4d3fb0e9004ec3b483706466/HC-12-Datasheet.pdf const uint8_t hc12RxPin = 9; // connnected to Nano acting as Tx const uint8_t hc12TxPin = 8; // connnected to Nano acting as Rx const uint8_t hc12SetPin = 7; // used to go to AT mode if connected to GND (10kΩ internal pullup, could float). AltSoftSerial HC12; const uint8_t messageMaxSize = 50; char hc12Message[messageMaxSize + 1] = {'\0'}; // +1 for trailing '\0' boolean waifForHC12Message(uint32_t timeout = 500) { uint8_t hc12MessageIndex = 0; boolean messageReady = false; uint32_t t0 = millis(); while (!messageReady && (millis() - t0 < timeout)) { int r = HC12.read(); if (r != -1) { t0 = millis(); if (r == '\n') { if (hc12MessageIndex == 0) hc12Message[0] = '\0'; //empty cString messageReady = true; } else if (r != '\r') { // ignore CR hc12Message[hc12MessageIndex++] = (char) r; hc12Message[hc12MessageIndex] = '\0'; // maintain cString if (hc12MessageIndex >= messageMaxSize) hc12MessageIndex = messageMaxSize - 1; // don't overrflow } } } return messageReady; } bool ATCommandMode = true; void setATCommandMode(bool mode) { digitalWrite(hc12SetPin, mode ? LOW : HIGH); // pull SET to LOW to activate AT command mode delay(mode ? 40 : 80); // according to specifications (40ms to be active, 80ms to save changes upon exit) ATCommandMode = mode; } bool tuneToHC12BaudRate(uint8_t& index) { uint8_t baudIndex = 0xFF; setATCommandMode(true); for (baudIndex = 0; baudIndex < nbBaudRates; baudIndex++) { HC12.begin(hc12BaudRates[baudIndex]); delay(1); HC12.println(F("AT")); if (waifForHC12Message() && !strncmp(hc12Message, "OK", 2)) { index = baudIndex; break; } else HC12.end(); } setATCommandMode(false); return baudIndex < nbBaudRates; } void initHC12() { uint8_t detectedBaudIndex = 0; if (tuneToHC12BaudRate(detectedBaudIndex)) { // managed to connect // Improvement check answer is OK for each command setATCommandMode(true); HC12.print(F("AT+FU")); HC12.println(hc12Parameters.fuMode); HC12.print(F("AT+P")); HC12.println(hc12Parameters.powerMode); HC12.print(F("AT+")); HC12.println(hc12Channels[hc12Parameters.channelIndex]); HC12.print(F("AT+B")); HC12.println(hc12BaudRates[hc12Parameters.baudRateIndex]); setATCommandMode(false); // exit AT Mode, which validates new settings HC12.end(); HC12.begin(hc12BaudRates[hc12Parameters.baudRateIndex]); HC12Ready = true; } else { lcd.clear(); lcd.print(F("HC12 unavailable")); for (uint8_t i = 0; i < 5; i++) { TimerFreeTone(buzzPin, 500, 50); delay(500); } while (true); } } void calculateDistanceCode() { // calculate beep code ('0' to '9') depending on proximity. '0' = continuous beep, '9' = no beep if (distance >= maxDistanceAlert_cm) distanceCode = maxDistanceBeepCode; else if (distance <= minDistanceAlert_cm) distanceCode = minDistanceBeepCode; else distanceCode = map(distance, minDistanceAlert_cm, maxDistanceAlert_cm, minDistanceBeepCode + 1, maxDistanceBeepCode); lcd.clear(); lcd.print(F("D:")); lcd.print(distance); lcd.print(F(" cm")); } void submitDistance() { static char previousDistanceCode = 'A'; static uint32_t previousSendChrono = 0; static uint32_t chronoNoBeep = 0; static bool sleepPillDelivered = false; if (HC12Ready) { if (distanceCode == maxDistanceBeepCode) { if (previousDistanceCode != maxDistanceBeepCode) { chronoNoBeep = millis(); HC12.write(distanceCode); previousSendChrono = millis(); previousDistanceCode = distanceCode; } else { if (!sleepPillDelivered && (millis() - chronoNoBeep >= 3000ul)) { // no detection for 3sec, sleep remote HC12.write(sleepPillCode); sleepPillDelivered = true; } } } else { // not max distance, need to inform slaves when needed if ((distanceCode != previousDistanceCode) || (millis() - previousSendChrono > 2000ul)) { // force keep alive update every 2s if (sleepPillDelivered) { HC12.write(wakeUpPillCode); // the first write will wake up the remote sleepPillDelivered = false; } HC12.write(distanceCode); previousSendChrono = millis(); previousDistanceCode = distanceCode; } } } } void audioFeedback() { static uint32_t lastTrigger; submitDistance(); if (distanceCode == minDistanceBeepCode) TimerFreeTone(buzzPin, buzFreq, 10); else if (distanceCode != maxDistanceBeepCode) { uint32_t deltaT = (distanceCode < '4' ? 100ul : 150ul) * (distanceCode - '0'); // (100ms x distance index + ∆t if further) in between two consecutive beeps if (millis() - lastTrigger >= deltaT) { TimerFreeTone(buzzPin, buzFreq, 50); // active wait lastTrigger = millis(); } } } void setup() { pinMode(hc12SetPin, OUTPUT); // LOW by default pinMode(buzzPin, OUTPUT); Serial.begin(sonarBaudRate); // init lcd lcd.begin(); lcd.backlight(); lcd.print(F("Welcome")); // get parameters from EEPROM getDefaults(); printDefaults(); // get HC12 ready (based on EEPROM defaults) initHC12(); // ready to Go lcd.setCursor(0, 1); lcd.print(F("Ready")); TimerFreeTone(buzzPin, 3000, 200); } void loop() { if (acquisition()) calculateDistanceCode(); audioFeedback(); }
——————————————————————————————————
Pour un esclave, le montage est plus simple:
Slave:
Câblage:
Alimentation:Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 0 Rx HC12 Tx 1 Tx HC12 Rx 2 3 4 5 6 Piezo + (through 150Ω current limiting resistor) 7 HC12 Set 8 9 10 11 12 13 Led
Code:
1
2 HC12 : 5V, GND Arduino : USB, 7-20V unregulated on pin 30 (Vin), 5V regulated on pin 27 (5V)
Comme on veut faire dormir l'Arduino quand il ne sert pas, il faut installer les Bibliothèques suivantes:
LowPower https://github.com/rocketscream/Low-Power
Le code:
IMPORTANT: Comme vous pouvez le voir dans la description des branchements, on a des appareils branchés sur les broches 0 et 1 (le Sonar ou le HC12). Cela présente un souci lors du chargement du code car cette pin est aussi connectée à l'USB ==> il faudra débrancher la broche 0 (Rx) pendant le téléchargement du code, puis rebrancher la broche et débrancher / rebrancher l'Arduino.Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250 // Author: Jay M for Arduino forums. // Standard MIT License applies. https://opensource.org/licenses/mit-license.php /* *** ARDUINO PIN MAPPING *** 0 Rx HC12 Tx 1 Tx HC12 Rx 2 3 4 5 6 Piezo + (through 150Ω current limiting resistor) 7 HC12 Set 8 9 10 11 12 13 Led A0 A1 A2 A3 A4 SCA A5 SCL *** POWER *** HC12 : 5V, GND Arduino : USB, 7-20V unregulated on pin 30 (Vin), 5V regulated on pin 27 (5V) *** WIRING *** Piezo : arduino --- 150Ω --- Piezo+ () Piezo- --- GND (to limit this initial inrush current) *** LIBRARIES *** LowPower https://github.com/rocketscream/Low-Power */ #include <LowPower.h> // https://github.com/rocketscream/Low-Power struct __attribute__ ((packed)) _paramS { uint8_t fuMode; // 1 to 4 uint8_t powerMode; // 1 to 8 uint8_t channelIndex; // 0 to 15 uint8_t baudRateIndex; // 0 to 7, index in hc12BaudRates array } hc12Parameters; const uint32_t hc12BaudRates[] = {9600, 4800, 1200, 2400, 19200, 38400, 57600, 115200}; // 9600 is the default so testing first const uint8_t nbBaudRates = sizeof hc12BaudRates / sizeof hc12BaudRates[0]; // set channels 6 bands apart to avoid interferences with nearbye devices const char* hc12Channels[] = {"C003", "C009", "C015", "C021", "C027", "C033", "C039", "C045", "C051", "C057", "C063", "C069", "C075", "C081", "C087", "C093"}; const uint8_t nbChannels = sizeof hc12Channels / sizeof hc12Channels[0]; // 16 to be selected through DIL DIP SWITCH const uint8_t hc12TxPin = 0; // connnected to Ardiuno Serial Rx (0) const uint8_t hc12RxPin = 1; // connnected to Ardiuno Serial Tx (1) const uint8_t hc12SetPin = 7; // used to go to AT mode if connected to GND (10kΩ internal pullup, could float) #define HC12 Serial bool HC12Ready = false; const uint8_t sleepPillCode = 'S'; // 'S' for sleep uint32_t keepAliveChrono; // last time we saw some HC12 activity const uint32_t maxSilenceBeforeSleep = 5000; // go to sleep after 5 seconds without any Serial update const uint8_t buzzPin = 6; // passive buzzer const uint16_t buzFreq = 1000; // frequency used for distance notification const uint16_t sleepFreq = 3000; // frequency used when module goes to sleep const uint16_t errorFreq = 300; // frequency used when eror is detected at boot const char minDistanceBeepCode = '0'; // continuous beep const char maxDistanceBeepCode = '9'; // no beep char distanceCode = maxDistanceBeepCode; // last distance code void getDefaults() { hc12Parameters.fuMode = 2; // AT+FU2 --> 80µA idle hc12Parameters.baudRateIndex = 1; // AT+B4800 --> 4800 bauds // TO DO: READ THOSE FROM DIL DIP SWITCHES INSTEAD OF CANNED VALUES hc12Parameters.powerMode = 4; // AT+P4 --> 8dBm, 6.3mW [8 values = 3 DIP SWITCHES] hc12Parameters.channelIndex = 0; // AT+C003 [16 values = 4 DIP SWITCHES] } // *** SLEEP MANAGEMENT *** void enablePinChangeInterrupt() { *digitalPinToPCMSK(hc12TxPin) |= bit(digitalPinToPCMSKbit(hc12TxPin)); // PCMSK2 |= bit(PCINT16); // enable PC interrupts for pin D0 PCIFR |= bit (digitalPinToPCICRbit(hc12TxPin)); // PCIFR |= bit(PCIF2); // clear any outstanding interrupt PCICR |= bit (digitalPinToPCICRbit(hc12TxPin)); // PCICR |= bit(PCIE2); // enable pin change interrupts for the group D0 to D7 } void disablePinChangeInterrupt() { *digitalPinToPCMSK(hc12TxPin) &= ~bit(digitalPinToPCMSKbit(hc12TxPin)); // PCMSK2 &= ~bit(PCINT16); // disable PC interrupts for pin D0 PCICR &= ~bit(digitalPinToPCICRbit(hc12TxPin)); // PCICR = ~bit(PCIE2); // disable pin change interrupts for the group D0 to D7 } ISR (PCINT2_vect) {} // handle pin change interrupt for D0 to D7 here void goToSleep() { for (uint8_t i = 0; i < 3; i++) { tone(buzzPin, sleepFreq, 50); delay(75); } digitalWrite(LED_BUILTIN, LOW); HC12.end(); pinMode(hc12TxPin, INPUT_PULLUP); enablePinChangeInterrupt(); LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF); disablePinChangeInterrupt(); HC12.begin(hc12BaudRates[hc12Parameters.baudRateIndex]); distanceCode = maxDistanceBeepCode; keepAliveChrono = millis(); } // *** HC12 MANAGEMENT *** void setATCommandMode(bool activate) { digitalWrite(hc12SetPin, activate ? LOW : HIGH); delay(activate ? 40 : 80); // according to specifications (40ms to be active, 80ms to save changes upon exit) } const uint8_t messageMaxSize = 50; char hc12Message[messageMaxSize + 1] = {'\0'}; // +1 for trailing '\0' boolean waifForHC12Message(uint32_t timeout = 500) { uint8_t hc12MessageIndex = 0; boolean messageReady = false; uint32_t t0 = millis(); hc12Message[0] = '\0'; //empty cString while (!messageReady && (millis() - t0 < timeout)) { int r = HC12.read(); if (r != -1) { t0 = millis(); if (r == '\n') messageReady = true; else if (r != '\r') { // ignore CR hc12Message[hc12MessageIndex++] = (char) r; hc12Message[hc12MessageIndex] = '\0'; // maintain cString if (hc12MessageIndex >= messageMaxSize) hc12MessageIndex = messageMaxSize - 1; // don't overrflow } } } return messageReady; } bool tuneToHC12BaudRate(uint8_t& index) { uint8_t baudIndex = 0xFF; setATCommandMode(true); for (baudIndex = 0; baudIndex < nbBaudRates; baudIndex++) { HC12.begin(hc12BaudRates[baudIndex]); delay(1); HC12.println(F("AT")); if (waifForHC12Message() && !strncmp(hc12Message, "OK", 2)) { index = baudIndex; break; } else HC12.end(); } setATCommandMode(false); return baudIndex < nbBaudRates; } void initHC12() { uint8_t detectedBaudIndex = 0; // get parameters from DIL DIP SWITCHES getDefaults(); if (tuneToHC12BaudRate(detectedBaudIndex)) { // managed to connect // Improvement neeeded = check answer is OK for each command setATCommandMode(true); HC12.print(F("AT+FU")); HC12.println(hc12Parameters.fuMode); HC12.print(F("AT+P")); HC12.println(hc12Parameters.powerMode); HC12.print(F("AT+")); HC12.println(hc12Channels[hc12Parameters.channelIndex]); HC12.print(F("AT+B")); HC12.println(hc12BaudRates[hc12Parameters.baudRateIndex]); setATCommandMode(false); // exit AT Mode, which validates new settings HC12.end(); HC12.begin(hc12BaudRates[hc12Parameters.baudRateIndex]); HC12Ready = true; } else { for (uint8_t i = 0; i < 3; i++) { tone(buzzPin, errorFreq, 200); delay(250); } while (true); } } void checkHC12() { if (HC12.peek() != -1) { // did we get an update from the Master? char received = HC12.read(); switch (received) { case sleepPillCode: goToSleep(); break; case minDistanceBeepCode ... maxDistanceBeepCode: keepAliveChrono = millis(); distanceCode = received; break; default: break; } } } void audioFeedback() { static uint32_t lastTrigger; if (distanceCode >= maxDistanceBeepCode) noTone(buzzPin); else if (distanceCode == minDistanceBeepCode) tone(buzzPin, buzFreq); else { uint32_t deltaT = (distanceCode < '4' ? 100ul : 150ul) * (distanceCode - '0'); // (100ms x distance index + ∆t if further) in between two consecutive beeps if (millis() - lastTrigger >= deltaT) { tone(buzzPin, buzFreq, 50); lastTrigger = millis(); } } } void checkKeepAlive() { static uint32_t blinkChrono = 0; if (millis() - keepAliveChrono > maxSilenceBeforeSleep) goToSleep(); // if there is no heart beat for a while, go to sleep. // show when we are not sleeping if (millis() - blinkChrono > 250) { digitalWrite(LED_BUILTIN, digitalRead(LED_BUILTIN) == LOW ? HIGH : LOW); blinkChrono = millis(); } } // --------------------- MAIN FEATURES --------------------- void setup() { pinMode(hc12SetPin, OUTPUT); // LOW by default (=> AT Command mode) pinMode(LED_BUILTIN, OUTPUT); // show heartbeat initHC12(); // get HC12 ready keepAliveChrono = millis(); } void loop() { checkHC12(); // did we receive something? audioFeedback(); // provide audio feedback when necessary checkKeepAlive(); // check if we need to go to sleep }
Le HC12 aura été configuré en mode FU2, il consomme donc 80µA en mode inactif. J'ai aussi câblé en dur la puissance à 4 soit 8dBm, 6.3mW. ça devrait aller pour 15/20m entre l'émetteur et les récepteurs.
ça devrait donc limiter la consommation électrique des esclaves.
Le canal choisi est C003. Pour l'instant non modifiable. L'idée c'est que sur le master on choisit le canal et la puissance par le biais du LCD et de l'encodeur rotatif.
Côté Slave on rajoutera un DIP Switch x 8 et on utiliser 3 pins pour la puissance (de 1 à 8) et 4 pins pour le choix du canal de communication entre Maître et Esclave. Cela nous donnera 16 choix que j'ai réparti dans le code comme suit:ils sont espacés de 6 en 6 ce qui donne une certaine robustesse aux interférences.Code:
1
2
3
4 "C003", "C009", "C015", "C021", "C027", "C033", "C039", "C045", "C051", "C057", "C063", "C069", "C075", "C081", "C087", "C093"
==> C'est la seule façon pour le moment d'appairer un master à ses slaves, donc 2 bateaux qui seraient équipés du même système sur le même canal et à la même puissance pour s'influencer s'ils sont assez proches... --> il faudra complexifier le protocole de communication pour embarquer un identifiant si on souhaite éviter ce souci.
Côté fonctionnement :
Le maître, quand il est allumé, ne s'endort pas (puisqu'il sera alimenté par le bateau).
Si vous allumez un esclave, s'il ne reçoit rien du maître en 5 secondes, il va se mettre en sommeil.
Quand vous allumez le maître ça réveille l'esclave (du moment que l'esclave était alimenté et en sommeil bien sûr)
Si la distance lue par le maître reste supérieure à 9m pendant plus de 3 secondes, le maître arrête d'envoyer des messages aux esclaves et leur dit de dormir.
Si la distance lue par le maître est inférieure à 9m, il envoie des messages aux esclaves à chaque changement de période de beep ou toutes les 2 secondes. Si les esclaves dormaient, ça les réveille.
Si vous éteignez le maître, comme les esclaves ne recevront plus rien, il se mettent en sommeil automatiquement au bout de 5s. (il vaut mieux les éteindre cependant physiquement)
Si à l'allumage un esclave ne trouve pas son HC12, il émet 3 beeps graves
Quand un esclave est réveillé la led de la carte clignote 2 fois par seconde
Quand un esclave se met en sommeil il émet 3 petits beeps aigus
Je n'ai pas le sonar chez moi - je l'ai simulé à la main en envoyant par la console série Rxxx<CR> puisqu'il semble que ce soit ce qu'envoie le Sonar (avec xxx la distance en cm). Si vous branchez un autre sonar et que le protocole est différent, dites le moi car il faudra que je modifie le code. J'ai testé pendant un moment, ça a l'air de tourner correctement.
Je n'ai pas mesuré la consommation électriques de l'esclave quand il dort (j'ai fait le test avec un UNO et même en sleep il consomme bcp).
voilà à vous de jouer :)
- intégrer ces écrans à la place du LCD "de base" ne devrait pas être trop compliqué.
- concernant les Arduinos, du côté des buzzer je mettrais plutôt un "Pro Mini" -> il n'a pas de port USB donc consomme bcp moins et on peut faire sauter le régulateur et la LED pour abaisser encore la consommation en mode sleep. Seule contrainte, c'est un peu plus pénible pour installer le code dessus car il faut passer par ISP ou un adaptateur série