GPS logger
Hello everyone! Some time ago I got the idea to upgrade my faithful and beloved GPS logger Holux M241. It would be possible to look for something interesting on the market that could satisfy my needs. But it was more interesting for me to dig in the direction of microcontrollers, NMEA GPS protocol, USB and SD Card wisdoms, thereby constructing the device of your dreams.
What exactly do I build I described in detail in the first part. At that stage, I was shooting for technology β I felt Arduino in the context of a relatively large project. It turned out there are a lot of nuances that in ordinary tutorials do not particularly affect. In the comments I received a lot of interesting intuition, for which I am very grateful to the readers. I hope today you will find something interesting.
This is the second article in the series. Like the previous one, it is a kind of construction magazine. I try to describe the technical solutions that I take in the course of work on the project. Today we will connect the GPS. And also to switch to more mature technologies β FreeRTOS and the microcontroller STM32. Well, as always, we will disassemble the firmware and watch whatβs written there.
GPSβΠΈΠΌ
By this time I already had the application framework. Everything was spinning on the Arduino Nano on the ATMega328 controller. Itβs time to connect my Beitan BN-880 GPS receiver.
I have some prejudiced attitude towards UART as a low-speed protocol from the last century. Reason, of course, I understand β the interface is simple as 3 pennies, it works on everything that moves. What else is needed? I also have a prejudiced attitude to text protocols β messages need to be parsed as well. Why not send data in binary form? Yes, even packages? Anyway, their person does not read. And binary packages could greatly simplify the processing. Well then I am, I hum.
Seeing the feet of SDA and SCK sticking out of the module I wanted to hang on to them. I grabbed it and β¦. Realized that the data is not so easy to get. I do not even know how. If UART is used, the GPS receiver simply populates the messages, and the recipient parsit what it needs. I2C same transfer is initiated only from the host side. Those. You need to form a certain query in order to get an answer. But what?
Googleβs Bug on BN-880 I2C did not give anything useful for a couple of hours. The people are just using UART, and most of the links led to quadro-cops forums and there were mostly quad-copter problems discussed there.
It was not so easy to get out on datasheets. Those. It was not entirely clear on which module to look for the datasheet. By indirect evidence, I found out that the UBlox NEO-M8N module is responsible for the GPS. It turned out that this thing knows how many features that the mother does not grieve (thereβs even a built-in odometer and a logger there). But I had to read 350 pages a lot.
I went through the dasad and I realized that I can not take this module with a pounce. I had to step on my throat and connect to the already verified UART. And then immediately enter into another problem: on the Arduin UART only one, and he sticks out in the direction of the company (fill in the firmware). I had to look in the direction of the SoftwareSerial library.
I wrote the simplest βrelinkerβ of messages from the GPS port in UART.
SoftwareSerial gpsSerial ( 10, 11); // RX, TX
Β
Void setup ()
{
Β Β Β Β Β Β Β Β Serial.begin (9600);
Β Β Β Β Β Β Β Β GpsSerial.begin (9600);
}
Β
Void loop ()
{
Β Β Β Β Β Β Β Β If (gpsSerial.available ()) {
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Serial.write (gpsSerial.read ());
Β Β Β Β Β Β Β Β }
}
Messages were poured, but satellites could not catch. Although the time was right.
$ GNRMC, 203954.00, V ,,,,,,,,,, N * 6A
$ GNVTG ,,,,,,,,, N * 2E
$ GNGGA, 203954.00 ,,,,, 0,00,99.99 ,,,,,, * 71
$ GNGSA, A, 1 ,,,,,,,,,,,,, 99.99.99.99.99.99 * 2E
$ GNGSA, A, 1 ,,,,,,,,,,,,, 99.99.99.99.99.99 * 2E
$ GPGSV, 1,1,02,02 ,,, 21,08 ,,, 09 * 7B
$ GLGSV, 1,1,00 * 65
$ GNGLL ,,,,, 203954.00, V, N * 5D
GPS lay for more than an hour at the window of 21 floors before issuing sane coordinates. And most of the review is not closed by high-rise buildings. There is a suspicion that the windows have a certain spraying, which worsens the quality of the signal. In any case, near the open window, satellites seem to catch faster.
If there is a signal, then you can parse. On the Internet, TinyGPSPlus was the first to be found. Has connected not without hacks. In ArduinoIDE everything worked, but at Atmel Studio did not want. I had to manually write the way to the library.
But then the problem got out. On simple sketches from the examples of TinyGPS + everything worked. But when I connected it to my project with the display and buttons everything broke. The device tangibly missed, obviously skipping the screenβs rendering. In the port monitor, I began to notice the crumbled messages from the GPS.
The first assumption was that SoftwareSerial very seriously wasted processor resources. So SoftwareSerial needs to be sent to the furnace, because For reliable communication with GPS it is not suitable (at least in the form in which it is in the examples). I even wanted to turn the scheme inside out: GPS connect to the hardware UARTβu arduiny, and the software series to use for debug (although if you have a screen to debug through the UART may not even be required). But with such a scheme, it will not be possible to download the firmware via UART. I had to get the USBAsp programmer.
But a little later I realized that the SoftwareSerial thing is gluttonous, but in this case the problem is not in it, but in the drawing function. Drawing the current screen takes 50-75ms (plus a bit more for overhead). SoftwareSerial works on receiving an interrupt on the leg controller and a lot, in general, it should not consume. But it has a receive buffer of only 64 bytes, which even at 9600 is filled in 60ms. It turns out that while the program is busy drawing the screen, some of the message from GPS is already passing by.
In the first half of the article, I get a lot of text. Iβll dilute them with pictures of them. This screen displays the current altitude and vertical speed
ARM
So. With the current approach, I rested at once in several restrictions:
- Flash and RAM. Not so much that was busy, but you had to constantly remember this
- There is only one UART. Additional SoftwareSerial consumes CPU resources significantly.
- Itβs clearly impossible to do everything in one thread. We need to think about parallelizing tasks.
And I also had to design with the expectation of the future β I still have the connection of USB and SD cards.
After the release of the previous part, I received many comments that Arduino sucks and the future behind ARM and STM32 controllers. I did not really want to leave the Arduino platform. As I said, the framework is fairly simple and understandable, and I also know the ATMega controllers well.
At the same time, the transition to STM32 would most likely mean a change in the platform as a whole, the microcontroller, the compiler, the framework, the libraries, the IDE and who knows what else. Those. Almost all at once. For the project, this would mean completely stop, study the documentation for a long time, study different examples, and only then start copying everything from scratch.
I began to feel the exits from the situation, listening to the commentators of the first part. I wanted to find a solution that solved the restrictions, gave some reserve for the future, but it did not require huge resources to move everything at once. Here are a few (in general, independent) things I went through.
- I connected a Sparkfun Pro Micro clone to ATMega32u4 (3.3V, 8MHz). In it, I wanted to touch the hardware USB. It took me quite a lot of time to start this thing at all. A regular bootloader did not really like to start up like an arduino, and fuse bits were put up in some mysterious way. As a result, using USBAsp sewed a bootloader from Arduino Leonardo and everything started.
- A debug board for ATMega64 came. It has 2 times more memory (both flash and RAM) and 2 uart. In principle, it removes restrictions. Unfortunately, there is no circuit attached to the board and what kind of quartz is there too is not clear. While postponed.
- I tried to touch the port of FreeRTOS under AVR. But here Kaku has put Atmel Studio. It turned out that she has 2 kinds of projects. In one studio works in Arduino mode, but in this case, practically nothing can be changed in the project settings. Those. Itβs trivial to even put FreeRTOS in a subdirectory and set the include path. It only knows how to add all the files in one heap, which would personally irritate me.The second option is the Generic C ++ Executable project type. It is understood that you need to write on bare C ++. Here you can already configure as your heart desires. But firstly it is necessary as that to fasten Arduinovsky frameworks, and secondly it is not clear how to fasten the filler of the firmware to the controller. Avrdude stubbornly did not want to overload the microcontroller in the bootloader (although the command line I peeked at ArduinoIDE using ProcessMonitor). I have USBasp, but if there is a USB port directly on the card, itβs like itβs not comme il faut.
- Finally, I combed the comb on the board with STM32F103C8T6 and, according to the instructions, installed STM32duino. To my surprise, the light-emitting diode on the LEDs immediately started working. To even more surprise, porting my project to a new controller took less than 10 minutes !!! In total, a couple of inkludov change and the number of pins to correct.
That was it. I was getting the power of STM32 (yes, the drawing function was now only 18ms long!) And I could still use the Arduino framework. This made it possible to continue working on the project, while, as necessary, smoothly dive into the new platform, reading in the metro on the microcontroller.
The increase in the flush is, in fact, a very illusory improvement. The project took half the flush on ATmega32, and it takes almost half on the new STM32 (okay, 26k from 64k). So you should not relax. Especially (as they write on the Internet), the compiled code is somewhat more sweeping and fills the flash faster than on the AVR. So just in case ordered a handkerchief with a 128k flush.
The truth here I was waiting for another surprise. The people on the Internet wrote that although the controller on datasheet has a 64K flush on board, in fact you can use 128k. Those. It seems that ST produces the same chip, only the part marks STM32F103C8T6, and another as STM32F103CBT6 (the same controller, but with a 128K flush).
By the way (follow up after the previous article). In the ARM architecture, both flash and RAM are in the same address space and are read in a single way. Therefore, dances with a tambourine and declaration of constants with the help of PROGMEM are no longer needed. Poole for the sake of cleanliness of the code. Tables of virtual functions, too, can not be copied anywhere; They are also in the same address space.
Another picture for diluting the text. From left to right: the direction of movement (now we are not going anywhere), the current speed, the current altitude. The screen is honestly licked with a similar one from Holux M241
FreeRTOSβim
The STM32duino also found a FreeRTOS port for my controller (and already two β 7.0.1 and 8.2.1). Examples with minimal corrections also earned. So you could switch to FreeRTOS without overwriting a significant part of the project.
After reading a couple of articles (one, two), I realized what power is now available to me β streams, mutexes, queues, semaphores and other synchronization. Itβs like on big computers. The main thing is to design everything correctly.
Despite the fact that the main problem I had was GPS, I still decided to start with something simpler β the buttons. In a sense, FreeRTOS makes code much simpler β each thread can deal with some specific task, and, if necessary, notify other threads. So, the task of servicing the buttons perfectly lodged in this ideology β listen to yourself buttons and do not get distracted by anything. Step aside
static void selButtonPinHandler ()
{
Β Β Β Β Β Β Β Static uint32 lastInterruptTime = 0;
Β
Β Β Β Β Β Β Β If (digitalRead (SEL_BUTTON_PIN)) // Falling edge
Β Β Β Β Β Β Β {
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Uint32 cur = millis ();
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Uint32 pressDuration = cur - lastInterruptTime;
Β
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Serial.print ("DePressed at");
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Serial.println (lastInterruptTime);
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β If (pressDuration> LONG_PRESS_TIMEOUT)
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Serial.println ("Sel Long Press");
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Else
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β If (pressDuration> SHORT_CLICK_TIMEOUT)
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Serial.println ("Sel Short Click");
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Else
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β {
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Serial.print ("Click was too short:");
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Serial.println ((int) pressDuration);
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β }
Β Β Β Β Β Β Β }
Β Β Β Β Β Β Β Β
Β Β Β Β Β Β Β Β
Β Β Β Β Β Β Β LastInterruptTime = millis ();
Β
Β Β Β Β Β Β Β If (! DigitalRead (SEL_BUTTON_PIN)) // Raising edge
Β Β Β Β Β Β Β {
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Serial.print ("Pressed at");
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Serial.println (lastInterruptTime);
Β Β Β Β Β Β Β }
}
Void initButtons ()
{
Β Β Β Β Β Β // Set up button pins
Β Β Β Β Β Β PinMode (SEL_BUTTON_PIN, INPUT_PULLUP); // TODO: using PullUps is an AVR legacy. Consider changing this to pull down
Β Β Β Β Β Β PinMode (OK_BUTTON_PIN, INPUT_PULLUP); // so pin state match human logic expectations
Β Β Β Β Β Β Β Β
Β Β Β Β Β Β AttachInterrupt (SEL_BUTTON_PIN, selButtonPinHandler, CHANGE);
}
Instead of prints, there had to be a sending of messages about the button that was pressed.
But honestly, it turned out to be cumbersome (itβs processing only one button) and itβs also terribly buggy. There were some kind of false alarms, or vice versa non-triggering. It seems that the function millis () did something wrong and could return the same values ββfor a fairly long time.
Let me remind you. In the main program cycle, I had a large state machine that controlled the display and listened to the buttons. Adding some kind of logic was accompanied by half of the code redrawing, and to understand how it works just looking at the code under the force was just the programming guru of the state machines. But since I have a PTM, everything worked out much easier.
// Pins assignment
Const uint8 SEL_BUTTON_PIN = PC14;
Const uint8 OK_BUTTON_PIN = PC15;
Β
// Timing constants
Const uint32 DEBOUNCE_DURATION = 1 / portTICK_PERIOD_MS;
Const uint32 LONG_PRESS_DURATION = 500 / portTICK_PERIOD_MS;
Const uint32 VERY_LONG_PRESS_DURATION = 1000 / portTICK_PERIOD_MS;
Const uint32 POWER_OFF_POLL_PERIOD = 1000 / portTICK_PERIOD_MS; // Polling very rare when power is off
Const uint32 IDLE_POLL_PERIOD = 100 / portTICK_PERIOD_MS; // And little more frequent if we are on
Const uint32 ACTIVE_POLL_PERIOD = 10 / portTICK_PERIOD_MS; // And very often when the user is active.
Β
QueueHandle_t buttonsQueue;
// Reading button state (perform debounce first)
Inline bool getButtonState (uint8 pin)
{
If (digitalRead (pin))
{
// dobouncing
VTaskDelay (DEBOUNCE_DURATION);
If (digitalRead (pin))
Return true;
}
Return false;
}
/// Return ID of the pressed button (perform debounce first)
ButtonID getPressedButtonID ()
{
If (getButtonState (SEL_BUTTON_PIN))
Return SEL_BUTTON;
If (getButtonState (OK_BUTTON_PIN))
Return OK_BUTTON;
Return NO_BUTTON;
}
// Initialize buttons related stuff
Void initButtons ()
{
// Set up button pins
PinMode (SEL_BUTTON_PIN, INPUT_PULLDOWN);
PinMode (OK_BUTTON_PIN, INPUT_PULLDOWN);
// Initialize buttons queue
ButtonsQueue = xQueueCreate (3, sizeof (ButtonMessage)); // 3 clicks more than enough
}
Β
// Buttons polling thread function
Void vButtonsTask (void * pvParameters)
{
For (;;)
{
// Wait for a button
ButtonID btn = getPressedButtonID ();
If (btn! = NO_BUTTON)
{
// Button pressed. Waiting for release
TickType_t startTime = xTaskGetTickCount ();
While (getPressedButtonID ()! = NO_BUTTON)
VTaskDelay (ACTIVE_POLL_PERIOD);
// Prepare message to send
ButtonMessage msg;
Msg.button = btn;
// calc duration
TickType_t duration = xTaskGetTickCount () - startTime;
If (duration> VERY_LONG_PRESS_DURATION)
Msg.event = BUTTON_VERY_LONG_PRESS;
Else
If (duration> LONG_PRESS_DURATION)
Msg.event = BUTTON_LONG_PRESS;
Else
Msg.event = BUTTON_CLICK;
// Send the message
XQueueSend (buttonsQueue, & msg, 0);
}
// TODO: Use different polling periods depending on the global system state (off / idle / active)
VTaskDelay (ACTIVE_POLL_PERIOD);
}
}
It turned out very compact and understandable. The functions are all very linear. Just in the loop, we interrogate the buttons and, based on the duration of the click, send the corresponding message.
I decided that I will have 3 kinds of pressing times:
- Short for selecting the corresponding menu item
- Long for a special action (for example, resetting the selected parameter)
- A very long press to turn the device on and off
By the way, I decided to connect the buttons not to the plus, but to the minus. Naturally pull-up resistors replaced by pull-down. I am not strong in electronics and I can make mistakes here, but in general I was guided by the following considerations:
- In the released position of the button, the pin is pressed to zero, and so the current does not flow (even miserable)
- When reading the value from pin, the value is obtained non-inverted: 1 if the button is pressed, 0 β released
ScreenManager is also much simpler. There was no need for a global display state. The flow of rendering is exclusively rendered and is controlled by messages from the buttons. He just waited for messages in the queue and worked through the received commands. And the waiting cycle itself was also done through the queue with the help of timeout in the function xQueueReceive. Those. The function waits for the message, and if nothing happens for a long time β just draws the screen as it is
void vUserInteractionTask (void * pvParameters)
{
Β Β Β Β Β Β Β For (;;)
Β Β Β Β Β Β Β {
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β // Poll the buttons queue for an event. Process button if pressed, or show current screen as usual if no button pressed
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β ButtonMessage msg;
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β If (xQueueReceive (buttonsQueue, & msg, DISPLAY_CYCLE))
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β ProcessButton (msg);
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β // Do what we need for the current state
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β DrawDisplay ();
Β Β Β Β Β Β Β }
}
It turned out, in my opinion, very elegant. Later, I added a screen shutdown after some timeout (saving the battery), but the code did not become much more complicated.
Button handling is just trivial β just parsim message and call the required function
void processButton (const ButtonMessage & msg)
{
Β Β Β Β Β Β If (msg.button == SEL_BUTTON && msg.event == BUTTON_CLICK)
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β GetCurrentScreen () -> onSelButton ();
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β
Β Β Β Β Β Β Β If (msg.button == OK_BUTTON && msg.event == BUTTON_CLICK)
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β GetCurrentScreen () -> onOkButton ();
Β
Β Β Β Β Β Β Β // TODO: process long press here
}
The showMessageBox () function has also become much simpler and has now become completely linear
void showMessageBox (const char * text)
{
Β Β Β Β Β Β Β Β // Center text
Β Β Β Β Β Β Β Β Uint8_t x = 128/2 - strlen_P (text) * 6/2;
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β
Β Β Β Β Β Β Β Β // Draw the message
Β Β Β Β Β Β Β Β Display.clearDisplay ();
Β Β Β Β Β Β Β Β Display.setFont (NULL);
Β Β Β Β Β Β Β Β Display.drawRect (2, 2, 126, 30, 1);
Β Β Β Β Β Β Β Β Display.setCursor (x, 12);
Β Β Β Β Β Β Β Β Display.print (text);
Β Β Β Β Β Β Β Β Display.display ();
Β Β Β Β Β Β Β Β
Β Β Β Β Β Β Β Β // Wait required duration
Β Β Β Β Β Β Β Β VTaskDelay (MESSAGE_BOX_DURATION);
}
And finally. What is this device, if it does not have a blinking light bulb? It is necessary to correct. No matter how ridiculous it was, itβs easy to see whether the device is still working on the blinking diode.
void vLEDFlashTask (void * pvParameters )
{
For (;;)
{
VTaskDelay (2000);
DigitalWrite (PC13, LOW);
VTaskDelay (100);
DigitalWrite (PC13, HIGH);
}
}
ΠΠΏΡΡΡ GPSβΠΈΠΌ
ΠΠ°ΠΊΠΎΠ½Π΅Ρ, Π½Π°ΡΡΠ°Π»ΠΎ Π²ΡΠ΅ΠΌΡ ΡΠ΅ΡΠ·Π°ΡΡ GPS. Π’Π΅ΠΏΠ΅ΡΡ ΡΠΆΠ΅ Π½Π΅Ρ ΠΏΡΠΎΠ±Π»Π΅ΠΌΡ ΠΎΠ΄Π½ΠΎΠ²ΡΠ΅ΠΌΠ΅Π½Π½ΠΎ ΡΠ»ΡΡΠ°ΡΡ GPS ΠΈ Π΄Π΅Π»Π°ΡΡ Π²ΡΠ΅ ΠΎΡΡΠ°Π»ΡΠ½ΠΎΠ΅. ΠΠ»Ρ Π½Π°ΡΠ°Π»Π° Ρ ΠΎΠΏΡΡΡ Π½Π°ΠΏΠΈΡΠ°Π» ΠΏΠ΅ΡΠ΅Π»ΠΈΠ²Π°ΡΠΎΡ:
void initGPS()
{
// GPS is attached to Serial1
Serial1.begin(9600);
}
void vGPSTask(void *pvParameters)
{
for (;;)
{
while(Serial1.available())
{
int c = Serial1.read();
gps.encode(c);
Serial.write(c);
}
vTaskDelay(5);
}
}
ΠΠΎ ΡΡΡ Π²ΠΎΠ·Π½ΠΈΠΊΠ»Π° ΠΏΡΠΎΠ±Π»Π΅ΠΌΠ°. Π‘ΠΎΠΎΠ±ΡΠ΅Π½ΠΈΡ ΡΠΎΡΠΌΠ°Π»ΡΠ½ΠΎ ΠΏΠ°ΡΡΠΈΠ»ΠΈΡΡ, ΡΠΎΠ»ΡΠΊΠΎ Π²ΠΎΡ Π΄Π°ΠΆΠ΅ Π²ΡΠ΅ΠΌΡ Π²ΡΠΊΡΡΠΈΡΡ ΠΎΡΡΡΠ΄Π° Π½Π΅ ΠΏΠΎΠ»ΡΡΠΈΠ»ΠΎΡΡ. ΠΡΡΠ΅Π½ΠΈΠ΅ ΠΈΡΡ ΠΎΠ΄Π½ΠΈΠΊΠΎΠ² TinyGPS ΠΈ Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΠΈ Π½Π° ΠΏΡΠΈΠ΅ΠΌΠ½ΠΈΠΊ ΠΏΠΎΠΊΠ°Π·Π°Π»ΠΎ Π½Π΅Π±ΠΎΠ»ΡΡΠΎΠ΅ Π½Π΅ΡΠΎΠΎΡΠ²Π΅ΡΡΡΠ²ΠΈΠ΅ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠΉ ΠΎΡ GPS ΠΌΠΎΠ΄ΡΠ»Ρ ΠΈ ΡΠ΅ΠΌ ΡΡΠΎ ΡΠΌΠ΅Π΅Ρ ΠΏΠ°ΡΡΠΈΡΡ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ°.
ΠΠΎΠ΄ΡΠ»Ρ UBlox ΡΠ΅Π°Π»ΠΈΠ·ΡΠ΅Ρ Π½Π΅ΠΊΠΎΠ΅ ΡΠ°ΡΡΠΈΡΠ΅Π½ΠΈΠ΅ ΠΏΡΠΎΡΠΎΠΊΠΎΠ»Π° NMEA. ΠΠ°ΠΆΠ΄ΠΎΠ΅ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠ΅ Π½Π°ΡΠΈΠ½Π°Π΅ΡΡΡ Ρ ΠΏΡΡΠΈΠ±ΡΠΊΠ²Π΅Π½Π½ΠΎΠ³ΠΎ ΠΈΠ΄Π΅Π½ΡΠΈΡΠΈΠΊΠ°ΡΠΎΡΠ° ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΡ.
$GNGGA,181220.00,,,,,0,00,99.99,,,,,,*70
ΠΠ΅ΡΠ²ΡΠ΅ 2 Π±ΡΠΊΠ²Ρ ΠΊΠΎΠ΄ΠΈΡΡΡΡ ΠΏΠΎΠ΄ΡΠΈΡΡΠ΅ΠΌΡ, ΠΊΠΎΡΠΎΡΠ°Ρ ΠΏΡΠΈΠ³ΠΎΡΠΎΠ²ΠΈΠ»Π° Π΄Π°Π½Π½ΡΠ΅: GP Π΄Π»Ρ GPS, GL Π΄Π»Ρ GLONASS, GA Π΄Π»Ρ GALILLEO. Π Π²ΠΎΡ Π΅ΡΠ»ΠΈ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ ΠΊΠΎΠΌΠ±ΠΈΠ½Π°ΡΠΈΡ ΡΠΈΡΡΠ΅ΠΌ ΠΏΠΎΠ·ΠΈΡΠΈΠΎΠ½ΠΈΡΠΎΠ²Π°Π½ΠΈΡ ΡΠΎ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΡ Π±ΡΠ΄ΡΡ Π½Π°ΡΠΈΠ½Π°ΡΡΡΡ Ρ GN.
ΠΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° TinyGPS+ Π½Π° ΡΠ°ΠΊΠΎΠ΅ ΡΠ°ΡΡΡΠΈΡΠ°Π½Π° Π½Π΅ Π±ΡΠ»Π° β ΠΎΠ½Π° ΡΠΌΠ΅Π»Π° ΠΏΠ°ΡΡΠΈΡΡ ΡΠΎΠ»ΡΠΊΠΎ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΡ GP. ΠΡΠΈΡΠ»ΠΎΡΡ Π΅Π΅ ΡΡΡΠΎΠΊ ΠΏΠΎΠ΄ΠΏΡΠ°Π²ΠΈΡΡ β ΠΏΠΎΠΌΠ΅Π½ΡΠ» ΡΠΎΠΎΡΠ²Π΅ΡΡΡΠ²ΡΡΡΡΡ ΡΡΡΠΎΠΊΡ Π² ΠΏΠ°ΡΡΠ΅ΡΠ΅ ΠΈ Π²ΡΠ΅ΠΌΡ Π½Π° ΡΠΊΡΠ°Π½Π΅ ΠΏΠΎΠ±Π΅ΠΆΠ°Π»ΠΎ. Π’ΠΎΠ»ΡΠΊΠΎ Π²ΠΎΡ ΡΡΠΎ Π²ΡΠ΅ ΠΏΠΎΠΏΠ°Ρ ΠΈΠ²Π°Π»ΠΎ ΠΊΠ°ΠΊΠΈΠΌ ΡΠΎ Ρ Π°ΠΊΠΎΠΌ.
Π’ΠΎΠ²Π°ΡΠΈΡ ΠΏΠΎΠ΄ΡΠΊΠ°Π·Π°Π» Π°Π»ΡΡΠ΅ΡΠ½Π°ΡΠΈΠ²Ρ β Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΡ NeoGPS. ΠΡΠΎ Π½Π°ΠΌΠ½ΠΎΠ³ΠΎ Π±ΠΎΠ»Π΅Π΅ ΡΠΈΡΠ°ΡΡΠ°Ρ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ°. ΠΠΎΠΌΠΈΠΌΠΎ ΡΠΎΠ³ΠΎ, ΡΡΠΎ ΠΎΠ½Π° ΡΠΌΠ΅Π΅Ρ ΠΏΠ°ΡΡΠΈΡΡ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΡ Ρ ΡΠ°Π·Π½ΡΠΌΠΈ ΠΏΡΠ΅ΡΠΈΠΊΡΠ°ΠΌΠΈ, ΠΎΠ½Π° Π΅ΡΠ΅ ΠΏΠΎΠ·Π²ΠΎΠ»ΡΠ΅Ρ ΠΏΠ°ΡΡΠΈΡΡ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ ΠΎ ΡΠΏΡΡΠ½ΠΈΠΊΠ°Ρ (Π»ΠΈΡΠ½ΠΎ ΠΌΠ½Π΅ Π½ΡΠ°Π²ΡΡΡΡ ΡΠ°ΠΊΠΈΠ΅ ΡΡΡΠΊΠΈ Π² GPS ΠΏΡΠΈΠ΅ΠΌΠ½ΠΈΠΊΠ°Ρ ). ΠΡΠ΅ ΡΡΠΎΠΈΡ ΠΎΡΠΌΠ΅ΡΠΈΡΡ, ΡΡΠΎ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° ΠΆΡΡΠΊΠΎ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΡΠ΅ΠΌΠ°Ρ β ΠΌΠΎΠΆΠ½ΠΎ Π²ΠΊΠ»ΡΡΠΈΡΡ/Π²ΡΠΊΠ»ΡΡΠΈΡΡ ΠΏΠ°ΡΡΠΈΠ½Π³ ΠΎΡΠ΄Π΅Π»ΡΠ½ΡΡ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠΉ ΠΈ ΡΠ΅ΠΌ ΡΠ°ΠΌΡΠΌ ΡΠ΅Π³ΡΠ»ΠΈΡΠΎΠ²Π°ΡΡ ΠΏΠΎΡΡΠ΅Π±Π»Π΅Π½ΠΈΠ΅ ΠΏΠ°ΠΌΡΡΠΈ Π² Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠΈ ΠΎΡ Π·Π°Π΄Π°Ρ.
ΠΠΎΠ΄ΠΊΠ»ΡΡΠΈΡΡ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΡ ΠΊ stm32duino ΡΡΡΠ΄Π° Π½Π΅ ΡΠΎΡΡΠ°Π²ΠΈΠ»ΠΎ, ΠΏΡΠ°Π²Π΄Π° ΡΡΡΠΎΠΊ ΠΏΠΎΠ΄ΠΏΠΈΠ»ΠΈΡΡ Π²ΡΠ΅ ΠΆΠ΅ ΠΏΡΠΈΡΠ»ΠΎΡΡ. ΠΠΎ ΠΊΠ°ΠΊ Π²ΡΠ΅Π³Π΄Π° Π² ΠΏΡΠΈΠΌΠ΅ΡΠ°Ρ Π²ΡΠ΅ ΠΏΡΠΎΡΡΠΎ ΠΈ ΠΏΠΎΠ½ΡΡΠ½ΠΎ, Π° Π² ΡΠ΅Π°Π»ΡΠ½ΠΎΠΌ ΠΏΡΠΎΠ΅ΠΊΡΠ΅ ΠΎΠ½ΠΎ ΡΡΠ°Π·Ρ Π½Π΅ Π·Π°ΡΠ°Π±ΠΎΡΠ°Π»ΠΎ. Π ΡΠ°ΡΡΠ½ΠΎΡΡΠΈ Π±ΡΠ»ΠΎ Π½Π΅ΡΡΠ½ΠΎ Π² ΠΊΠ°ΠΊΠΎΠΉ ΠΌΠΎΠΌΠ΅Π½Ρ Π²ΡΠ΅ΠΌΠ΅Π½ΠΈ ΠΏΡΠ°Π²ΠΈΠ»ΡΠ½ΠΎ ΡΠΈΡΠ°ΡΡ ΠΈΠ· GPS. ΠΠΎΡ, Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ, ΠΏΠΎΠΏΡΡΠΊΠ° Π²ΡΡΠΈΡΠ°ΡΡ Π΄Π°Π½Π½ΡΠ΅ ΠΎ ΡΠΏΡΡΠ½ΠΈΠΊΠ°Ρ .
for (;;)
{
while(Serial1.available())
{
int c = Serial1.read();
Serial.write(c);
gpsParser.handle(c);
}
Β Β Β Β Β Β Β if(gpsParser.available())
Β Β Β Β Β Β Β {
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β memcpy(satellites, gpsParser.satellites, sizeof(satellites));
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β sat_count = gpsParser.sat_count;
Β Β Β Β Β Β Β }
vTaskDelay(10);
}
ΠΡΠ΅ΠΌΡ ΠΎΡ Π²ΡΠ΅ΠΌΠ΅Π½ΠΈ ΠΏΠ°ΡΡΠ΅Ρ Π³ΠΎΠ²ΠΎΡΠΈΡ, ΡΡΠΎ Π΄Π°Π½Π½ΡΠ΅ ΠΏΡΠΈΠ±ΡΠ»ΠΈ β Π·Π°Π±ΠΈΡΠ°ΠΉΡΠ΅. ΠΠΎΠΎΡΠ΄ΠΈΠ½Π°ΡΡ Π²ΡΠ΅Π³Π΄Π° ΠΏΡΠΈΠ΅Π·ΠΆΠ°ΡΡ Π½ΠΎΡΠΌΠ°Π»ΡΠ½ΠΎ, Π° Π²ΠΎΡ ΡΠΎ ΡΠΏΡΡΠ½ΠΈΠΊΠ°ΠΌΠΈ Π±Π΅Π΄Π°. ΠΠ°Π±ΠΈΡΠ°Ρ, Π° ΡΠ°ΠΌ Π½ΡΠ»ΠΈ. ΠΠ»ΠΈ Π½Π΅ Π½ΡΠ»ΠΈ. ΠΠ°ΠΊ ΠΏΠΎΠ²Π΅Π·Π΅Ρ.
ΠΠΊΠ°Π·Π°Π»ΠΎΡΡ Π½ΡΠΆΠ½ΠΎ Π±ΡΠ»ΠΎ Π²Π½ΠΈΠΌΠ°ΡΠ΅Π»ΡΠ½ΠΎ ΠΏΡΠΎΡΠΈΡΠ°ΡΡ Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΡ. ΠΡΠ΅ Π΄Π΅Π»ΠΎ Π² Π΄ΠΈΠ·Π°ΠΉΠ½Π΅ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΈ. ΠΠΎ ΠΈΠΌΡ ΡΠΊΠΎΠ½ΠΎΠΌΠΈΠΈ ΠΏΠ°ΠΌΡΡΠΈ Π΄Π°Π½Π½ΡΠ΅ ΡΠ°ΡΠΊΠ»Π°Π΄ΡΠ²Π°ΡΡΡΡ ΠΏΠΎ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΠΌ ΠΏΠΎ Ρ ΠΎΠ΄Ρ ΠΏΠ°ΡΡΠΈΠ½Π³Π°. ΠΡΠΈ ΡΠ΅ΠΌ ΠΏΠΎΠ±Π°ΠΉΡΠΎΠ²ΠΎ β ΠΏΡΠΈΡΠ΅Π» Π±Π°ΠΉΡ, ΠΎΠ±Π½ΠΎΠ²ΠΈΠ»ΠΈ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ. ΠΠ°Π½Π½ΡΠ΅ ΠΏΡΠΈΡ ΠΎΠ΄ΡΡ ΠΏΠ°ΠΊΠ΅ΡΠ°ΠΌΠΈ ΠΏΠΎ Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΎ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠΉ. ΠΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° NeoGPS Π΄ΠΎΠ»ΠΆΠ½Π° Π·Π½Π°ΡΡ ΠΊΠΎΠ³Π΄Π° Π½Π°ΡΠΈΠ½Π°Π΅ΡΡΡ Π½ΠΎΠ²ΡΠΉ ΠΏΠ°ΠΊΠ΅Ρ, ΡΡΠΎΠ±Ρ ΠΎΠ±Π½ΡΠ»ΠΈΡΡ Π²Π½ΡΡΡΠ΅Π½Π½ΠΈΠ΅ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΠ΅. ΠΠ° ΡΡΠΎ ΠΎΡΠ²Π΅ΡΠ°Π΅Ρ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ LAST_SENTENCE_IN_INTERVAL
//------------------------------------------------------
// Select which sentence is sent *last* by your GPS device
// in each update interval. This can be used by your sketch
// to determine when the GPS quiet time begins, and thus
// when you can perform "some" time-consuming operations.
#define LAST_SENTENCE_IN_INTERVAL NMEAGPS::NMEA_RMC
Π’Π°ΠΊ Π²ΠΎΡ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠ΅ RMC Ρ ΠΌΠ΅Π½Ρ ΠΏΡΠΈΡ ΠΎΠ΄ΠΈΡ ΡΠ°ΠΌΡΠΌ ΠΏΠ΅ΡΠ²ΡΠΌ Π² ΠΏΠ°ΠΊΠ΅ΡΠ΅ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠΉ. ΠΠΎΠ»ΡΡΠ°Π΅ΡΡΡ ΡΡΠΎ ΠΌΠΎΠΉ ΠΊΠΎΠ΄ ΠΌΠΎΠ³ ΠΏΡΠΎΡΠΈΡΠ°ΡΡ ΡΠ°ΡΡΠΈΡΠ½ΠΎ ΡΠ°ΡΠΏΠ°ΡΡΠ΅Π½ΡΠ΅ Π΄Π°Π½Π½ΡΠ΅ (ΠΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ ΡΡΠΎ Π±ΡΠ»ΠΈ Π΄Π°Π½Π½ΡΠ΅ ΠΏΡΠ΅Π΄ΡΠ΄ΡΡΠΈΡ ΠΏΠ°ΠΊΠ΅ΡΠΎΠ², ΠΊΠΎΡΠΎΡΡΠ΅ Π΅ΡΠ΅ Π½Π΅ ΡΡΠΏΠ΅Π»ΠΈ ΠΎΠ±Π½ΡΠ»ΠΈΡΡΡ). ΠΠ»ΠΈ Π²ΡΡΠΈΡΡΠ²Π°ΡΡ Π½ΡΠ»ΠΈ, Π΅ΡΠ»ΠΈ ΠΏΡΠΎΡΠΈΡΠ°ΡΡ Π² Π½Π΅ΡΠ΄Π°ΡΠ½ΠΎΠ΅ Π²ΡΠ΅ΠΌΡ. ΠΠ΅ΡΠΈΡΡΡ Π΄ΠΎΠ²ΠΎΠ»ΡΠ½ΠΎ ΠΏΡΠΎΡΡΠΎ: ΡΠΊΠ°Π·ΡΠ²Π°Π΅ΠΌ, ΡΡΠΎ Π² ΠΊΠ°ΠΆΠ΄ΠΎΠΌ ΠΏΠ°ΠΊΠ΅ΡΠ΅ ΠΎΡ GPS ΠΌΠΎΠ΄ΡΠ»Ρ ΠΏΠΎΡΠ»Π΅Π΄Π½Π΅Π΅ ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΠ΅ Ρ Π½Π°Ρ GLL.
Π‘ΠΏΡΡΠ½ΠΈΠΊΠΎΠ² ΠΌΠ½ΠΎΠ³ΠΎ, Π° ΡΠΈΠΊΡΠ° Π²ΡΠ΅ Π½Π΅Ρ ΠΈ Π½Π΅Ρ. Π‘Π²Π΅ΡΡ
Ρ Π²Π½ΠΈΠ·: ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ ΡΠΏΡΡΠ½ΠΈΠΊΠΎΠ² (ΠΎΡΡΠ»Π΅ΠΆΠΈΠ²Π°Π΅ΠΌΡΠ΅ vs Π½Π΅ΠΎΡΡΠ»Π΅ΠΆΠΈΠ²Π°Π΅ΠΌΡΠ΅ β Π½Π΅ Π·Π½Π°Ρ ΡΡΠΎ ΡΡΠΎ Π·Π½Π°ΡΠΈΡ), HDOP/VDOP, Π‘ΡΠ°ΡΡΡ GPS ΡΠΈΠ³Π½Π°Π»Π° (ΡΠ»ΠΎΠ²ΠΈΠ»ΠΎ/Π½Π΅ ΡΠ»ΠΎΠ²ΠΈΠ»ΠΎ)
ΠΡΡΠ°ΡΠΈ, Ρ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΎΠΉ Π² ΠΊΠΎΠΌΠΏΠ»Π΅ΠΊΡΠ΅ ΠΎΠ±Π½Π°ΡΡΠΆΠΈΠ»ΠΈΡΡ Π΄ΠΎΠ²ΠΎΠ»ΡΠ½ΠΎ ΡΠ΄ΠΎΠ±Π½ΡΠ΅ ΡΡΠ½ΠΊΡΠΈΠΈ ΠΏΠΎ ΡΠ°Π±ΠΎΡΠ΅ Ρ Π΄Π°ΡΠΎΠΉ ΠΈ Π²ΡΠ΅ΠΌΠ΅Π½Π΅ΠΌ. Π’Π°ΠΊ, Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ, ΠΎΡΠ΅Π½Ρ Π»Π΅Π³ΠΊΠΎ Π±ΡΠ»ΠΎ ΠΏΡΠΈΠΊΡΡΡΠΈΡΡ ΡΠ°ΡΠΎΠ²ΠΎΠΉ ΠΏΠΎΡΡ. Π― ΡΠΎΠ»ΡΠΊΠΎ Ρ ΡΠ°Π½Ρ Π²ΡΠ΅ΠΌΠ΅Π½Π½ΠΠ΅ ΡΠΌΠ΅ΡΠ΅Π½ΠΈΠ΅ Π² ΠΌΠΈΠ½ΡΡΠ°Ρ , Π° ΠΎΡΡΠ°Π»ΡΠ½ΠΎΠ΅ Π»Π΅Π³ΠΊΠΎ Π²ΡΡΡΠΈΡΠ°ΡΡ ΠΏΠΎ Ρ ΠΎΠ΄Ρ.
void TimeZoneScreen::drawScreen() const
{
// Get the date/time adjusted by selected timezone value
gps_fix gpsFix = gpsDataModel.getGPSFix();
int16 timeZone = getCurrentTimeZone();
NeoGPS::time_t dateTime = gpsFix.dateTime + timeZone * 60; //timeZone is in minutes
...
printNumber(dateBuf, dateTime.date, 2);
printNumber(dateBuf+3, dateTime.month, 2);
printNumber(dateBuf+6, dateTime.year, 2);
ΠΠΊΡΠ°Π½ Π²ΡΠ±ΠΎΡΠ° ΡΠ°ΡΠΎΠ²ΠΎΠ³ΠΎ ΠΏΠΎΡΡΠ° ΡΠ΅ΡΡΠ½ΠΎ ΡΠ»ΠΈΠ·Π°Π½ Ρ HuluxβΠ°
Model-ViewβΠΈΠΌ
ΠΡΠΈ Π½Π°ΠΏΠΈΡΠ°Π½ΠΈΡ ΠΊΠΎΠ΄Π° ΡΠ΅ΠΏΠ΅ΡΡ Π½Π΅Π»ΡΠ·Ρ Π·Π°Π±ΡΠ²Π°ΡΡ, ΡΡΠΎ ΠΌΡ ΡΠ°Π±ΠΎΡΠ°ΠΌ Π² ΠΌΠ½ΠΎΠ³ΠΎΠΏΠΎΡΠΎΡΠ½ΠΎΠΉ ΡΡΠ΅Π΄Π΅. Π’Π°ΠΊ, Ρ ΠΌΠ΅Π½Ρ Π΅ΡΡΡ ΠΏΠΎΡΠΎΠΊ, ΠΊΠΎΡΠΎΡΡΠΉ ΠΎΠ±ΡΠ»ΡΠΆΠΈΠ²Π°Π΅Ρ GPS: ΡΠ»ΡΡΠ°Π΅Ρ Serial ΠΏΠΎΡΡ, ΠΏΠΎΠ±Π°ΠΉΡΠΎΠ²ΠΎ ΠΏΠ°ΡΡΠΈΡ ΠΈΠ· Π½Π΅Π³ΠΎ Π΄Π°Π½Π½ΡΠ΅. ΠΠ°ΠΊΠ΅ΡΡ ΠΏΡΠΈΡ ΠΎΠ΄ΡΡ ΡΠ°Π· Π² ΡΠ΅ΠΊΡΠ½Π΄Ρ. ΠΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° Π·Π½Π°Π΅Ρ, ΠΊΠΎΠ³Π΄Π° Π½Π°ΡΠΈΠ½Π°Π΅ΡΡΡ ΡΠ»Π΅Π΄ΡΡΡΠΈΠΉ ΠΏΠ°ΠΊΠ΅Ρ ΠΈ ΠΏΠ΅ΡΠ΅Π΄ ΠΏΡΠΈΠ΅ΠΌΠΎΠΌ ΠΎΠ±Π½ΡΠ»ΡΠ΅Ρ Π²Π½ΡΡΡΠ΅Π½Π½ΠΈΠ΅ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΠ΅. ΠΠΎΠ³Π΄Π° ΠΏΠ°ΠΊΠ΅Ρ ΠΏΠΎΠ»Π½ΠΎΡΡΡΡ ΠΏΡΠΈΠ½ΡΡ Π²ΡΡΡΠ°Π²Π»ΡΠ΅ΡΡΡ ΡΠ»Π°Π³ available. ΠΠ°Π½Π½ΡΠ΅ ΠΏΡΠΈΠ΅Π·ΠΆΠ°ΡΡ Π½Π° ΠΏΡΠΎΡΡΠΆΠ΅Π½ΠΈΠΈ ΠΏΡΠΈΠΌΠ΅ΡΠ½ΠΎ Π·Π° ΠΏΠΎΠ»ΡΠ΅ΠΊΡΠ½Π΄Ρ (ΡΠ°ΠΌ Π±Π°ΠΉΡ 600 Π½Π° ΡΠΊΠΎΡΠΎΡΡΠΈ 9600). Π£ Π½Π°Ρ Π΅ΡΡΡ Π΅ΡΠ΅ ΠΏΠΎΠ»ΡΠ΅ΠΊΡΠ½Π΄Ρ, ΡΡΠΎΠ±Ρ ΠΈΡ Π·Π°Π±ΡΠ°ΡΡ, ΠΏΡΠ΅ΠΆΠ΄Π΅ ΡΠ΅ΠΌ Π½Π°ΡΠ½Π΅ΡΡΡ ΠΏΠ΅ΡΠ΅Π΄Π°ΡΠ° ΡΠ»Π΅Π΄ΡΡΡΠ΅Π³ΠΎ ΠΏΠ°ΠΊΠ΅ΡΠ°.
ΠΡΠΎΡΠΎΠΉ ΠΏΠΎΡΠΎΠΊ Π·Π°Π½ΠΈΠΌΠ°Π΅ΡΡΡ ΠΎΠ±ΡΠ»ΡΠΆΠΈΠ²Π°Π½ΠΈΠ΅ΠΌ Π΄ΠΈΡΠΏΠ»Π΅Ρ. Π¦ΠΈΠΊΠ» ΠΎΡΡΠΈΡΠΎΠ²ΠΊΠΈ ΠΏΡΠΎΠΈΡΡ ΠΎΠ΄ΠΈΡ ΠΊΠ°ΠΆΠ΄ΡΠ΅ 100-120ΠΌΡ. ΠΠ° ΠΊΠ°ΠΆΠ΄ΠΎΠΉ ΠΈΡΠ΅ΡΠ°ΡΠΈΠΈ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΠ° Π±Π΅ΡΠ΅Ρ Π°ΠΊΡΡΠ°Π»ΡΠ½ΡΠ΅ Π΄Π°Π½Π½ΡΠ΅ ΠΈΠ· GPS ΠΈ ΠΎΡΡΠΈΡΠΎΠ²ΡΠ²Π°Π΅Ρ ΡΠΎ, ΡΡΠΎ ΡΠ΅ΠΉΡΠ°Ρ Ρ ΠΎΡΠ΅Ρ Π²ΠΈΠ΄Π΅ΡΡ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ β ΠΊΠΎΠΎΡΠ΄ΠΈΠ½Π°ΡΡ, ΡΠΊΠΎΡΠΎΡΡΡ, Π²ΡΡΠΎΡΡ ΠΈΠ»ΠΈ ΡΡΠΎ Π½ΠΈΠ±ΡΠ΄Ρ Π΅ΡΠ΅. Π ΡΡΡ Π²ΠΎΠ·Π½ΠΈΠΊΠ°Π΅Ρ ΠΏΡΠΎΡΠΈΠ²ΠΎΡΠ΅ΡΠΈΠ΅: ΠΏΠΎΡΠΎΠΊ Π΄ΠΈΡΠΏΠ»Π΅Ρ Ρ ΠΎΡΠ΅Ρ ΠΏΠΎΠ»ΡΡΠ°ΡΡ Π΄Π°Π½Π½ΡΠ΅ Π²ΡΠ΅Π³Π΄Π°, ΡΠΎΠ³Π΄Π° ΠΊΠ°ΠΊ Π² Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ΅ ΠΎΠ½ΠΈ Π΄ΠΎΡΡΡΠΏΠ½Ρ ΡΠΎΠ»ΡΠΊΠΎ ΠΏΠΎΠ»ΡΠ΅ΠΊΡΠ½Π΄Ρ, Π° ΠΏΠΎΡΠΎΠΌ ΠΏΠ΅ΡΠ΅Π·Π°ΡΠΈΡΠ°ΡΡΡΡ.
Π Π΅ΡΠ΅Π½ΠΈΠ΅ Π΄ΠΎΡΡΠ°ΡΠΎΡΠ½ΠΎ ΠΎΡΠ΅Π²ΠΈΠ΄Π½ΠΎΠ΅: ΡΠΊΠΎΠΏΠΈΡΠΎΠ²Π°ΡΡ Π΄Π°Π½Π½ΡΠ΅ ΠΊ ΡΠ΅Π±Π΅ Π² ΠΏΡΠΎΠΌΠ΅ΠΆΡΡΠΎΡΠ½ΡΠΉ Π±ΡΡΠ΅Ρ. ΠΡΡΠ΅ΡΡΠ²Π΅Π½Π½ΠΎ Π΄Π°Π½Π½ΡΠ΅ Π² ΡΡΠΎΠΌ Π±ΡΡΠ΅ΡΠ΅ Π½ΡΠΆΠ½ΠΎ Π·Π°ΡΠΈΡΠΈΡΡ ΠΌΡΡΠ΅ΠΊΡΠΎΠΌ (mutex), ΠΈΠ½Π°ΡΠ΅ Π΄Π°Π½Π½ΡΠ΅ ΠΌΠΎΠ³ΡΡ Π±ΡΡΡ Π²ΡΡΠΈΡΠ°Π½Ρ Π½Π΅ΠΊΠΎΡΡΠ΅ΠΊΡΠ½ΠΎ. ΠΠΎ Π²ΠΎΡ Π² ΡΠ΅ΠΌ ΠΏΡΠΎΠ±Π»Π΅ΠΌΠ°. ΠΠ°Π½Π½ΡΠ΅ Π² ΠΏΠΎΡΠΎΠΊΠ΅ GPS ΠΏΠΎΡΠ²Π»ΡΡΡΡΡ Ρ ΠΎΡΡ ΠΈ ΡΠ΅Π΄ΠΊΠΎ, Π½ΠΎ Π²ΡΡΠΈΡΠ°ΡΡ ΠΈΡ ΠΌΠΎΠΆΠ½ΠΎ Π±ΡΡΡΡΠΎ (ΡΠ°ΠΌ Π²ΡΠ΅Π³ΠΎ ΠΏΠΎΠ»ΡΠΎΡΡ ΡΠΎΡΠ½ΠΈ Π±Π°ΠΉΡ ΠΏΠΎΡΠ»Π΅ ΠΏΠ°ΡΡΠΈΠ½Π³Π°), ΠΌΡΡΠ΅ΠΊΡ Π½Π°Π΄ΠΎΠ»Π³ΠΎ Π±Π»ΠΎΠΊΠΈΡΠΎΠ²Π°ΡΡ Π½Π΅ Π½ΡΠΆΠ½ΠΎ. Π Π²ΠΎΡ ΡΡΠ½ΠΊΡΠΈΡ ΡΠΈΡΠΎΠ²Π°Π½ΠΈΡ ΠΌΠΎΠΆΠ΅Ρ ΡΠ°Π±ΠΎΡΠ°ΡΡ Π΄ΠΎΠ²ΠΎΠ»ΡΠ½ΠΎ Π΄ΠΎΠ»Π³ΠΎ (Π΄ΠΎ 20ΠΌΡ). ΠΠ»ΠΎΠΊΠΈΡΠΎΠ²Π°ΡΡ ΠΌΡΡΠ΅ΠΊΡ Π½Π° ΡΠ°ΠΊΠΎΠ΅ Π΄Π»ΠΈΡΠ΅Π»ΡΠ½ΠΎΠ΅ Π²ΡΠ΅ΠΌΡ, Π² ΠΎΠ±ΡΠ΅ΠΌ ΡΠΎ, Π½Π΅ ΡΠΈΠ»ΡΠ½ΠΎ Ρ ΠΎΡΠΎΡΠΎ. Π₯ΠΎΡΡ ΠΈ Π½Π΅ ΡΠΌΠ΅ΡΡΠ΅Π»ΡΠ½ΠΎ, Π² ΡΡΠΎΠΌ ΠΊΠΎΠ½ΠΊΡΠ΅ΡΠ½ΠΎΠΌ ΠΏΡΠΎΠ΅ΠΊΡΠ΅.
ΠΠΎΠΆΠ½ΠΎ, ΠΊΠΎΠ½Π΅ΡΠ½ΠΎ, Π±ΡΡΡΡΠ΅Π½ΡΠΊΠΎ Π·Π°Π±Π»ΠΎΠΊΠΈΡΠΎΠ²Π°ΡΡ ΠΌΡΡΠ΅ΠΊΡ, Π·Π°Π±ΡΠ°ΡΡ Π΄Π°Π½Π½ΡΠ΅ Π² Π»ΠΎΠΊΠ°Π»ΡΠ½ΡΡ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ ΠΈ ΠΎΡΠΏΡΡΡΠΈΡΡ ΠΌΡΡΠ΅ΠΊΡ. ΠΠΎ ΡΡΠΎ ΡΡΠ΅Π²Π°ΡΠΎ ΠΏΠ΅ΡΠ΅ΡΠ°ΡΡ ΠΎΠ΄ΠΎΠΌ ΠΏΠ°ΠΌΡΡΠΈ. ΠΡΠ΅ ΠΏΠΎΠ»ΡΠΎΡΡ ΡΠΎΡΠ½ΠΈ Π±Π°ΠΉΡ ΠΏΡΠΈ 20 ΠΊΠΈΠ»ΠΎΠ±Π°ΠΉΡΠ°Ρ ΡΡΠΎ ΡΠΈΠ³Π½Ρ, Π½ΠΎ Π»ΠΈΡΠ½ΠΎ ΠΌΠ΅Π½Ρ Π½Π°ΠΏΡΡΠ³Π°Π΅Ρ ΡΠ°ΠΌ ΡΠ°ΠΊΡ ΡΡΠΎΠΉΠ½ΠΎΠΉ Π±ΡΡΠ΅ΡΠΈΠ·Π°ΡΠΈΠΈ.
ΠΡΡΠ΅Ρ, ΠΊΡΡΠ°ΡΠΈ, ΠΏΡΠΈΡΠ»ΠΎΡΡ ΠΎΠ±ΡΡΠ²ΠΈΡΡ Π³Π»ΠΎΠ±Π°Π»ΡΠ½ΠΎΠΉ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΠΎΠΉ ΠΈΠ±ΠΎ ΠΎΠ½ ΠΎΡΠ΅Π½Ρ Π±ΠΎΠ»ΡΡΠΎΠΉ ΠΈ Π²ΡΠ·ΡΠ²Π°Π΅Ρ ΠΏΠ΅ΡΠ΅ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ ΡΡΠ΅ΠΊΠ°, Π΅ΡΠ»ΠΈ ΠΎΠ±ΡΡΠ²Π»ΡΡΡ Π΅Π³ΠΎ Π² ΡΡΠ½ΠΊΡΠΈΠΈ. ΠΠ° Π²ΡΡΠΊΠΈΠΉ ΡΠ»ΡΡΠ°ΠΉ ΠΏΠΎΡΠΎΠΊΡ ΡΠΈΡΠΎΠ²Π°Π½ΠΈΡ Π²ΡΠΏΠΈΡΠ°Π» ΡΡΠ΅ΠΊΠ° ΠΏΠΎΠ±ΠΎΠ»ΡΡΠ΅.
NMEAGPS::satellite_view_t l_satellites[ NMEAGPS_MAX_SATELLITES ];
uint8_t l_sat_count;
Β
void SatellitesScreen::drawScreen()
{
Β Β Β xSemaphoreTake(xGPSDataMutex, portMAX_DELAY);
Β Β Β memcpy(l_satellites, satellites, sizeof(l_satellites));
Β Β Β l_sat_count = sat_count;
Β Β Β xSemaphoreGive(xGPSDataMutex);
Β Β Β
Β display.draw(....)
Β ...
}
Π‘ ΠΌΠ³Π½ΠΎΠ²Π΅Π½Π½ΡΠΌΠΈ Π·Π½Π°ΡΠ΅Π½ΠΈΡΠΌΠΈ, ΠΊΠΎΡΠΎΡΡΠ΅ ΠΌΠΎΠΆΠ½ΠΎ Π΄ΠΎΡΡΠ°ΡΡ ΠΏΡΡΠΌΠΎ ΠΈΠ· NMEA ΠΏΠΎΡΠΎΠΊΠ° Π²ΡΠ΅ ΠΏΡΠΎΡΡΠΎ β Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° NeoGPS ΠΈΡ Π²ΡΡΠΈΡΡΠ²Π°Π΅Ρ ΠΈ ΡΠ°ΡΠΊΠ»Π°Π΄ΡΠ²Π°Π΅Ρ ΠΏΠΎ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΠΌ. ΠΠ°ΠΆΠ΄ΡΠΉ ΡΠΊΡΠΈΠ½ ΠΌΠΎΠΆΠ΅Ρ ΠΏΡΠΎΡΡΠΎ ΠΏΡΠΎΡΠΈΡΠ°ΡΡ ΡΠΎΠΎΡΠ²Π΅ΡΡΡΠ²ΡΡΡΡΡ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ (Π½Π΅ Π·Π°Π±ΡΠ²Π°Ρ ΠΏΡΠΎ ΡΠΈΠ½Ρ ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΡ, ΠΊΠΎΠ½Π΅ΡΠ½ΠΎ) ΠΈ ΠΎΡΠΎΠ±ΡΠ°Π·ΠΈΡΡ Π΅Π΅ Π½Π° ΡΠΊΡΠ°Π½Π΅. ΠΠΎ Π²ΠΎΡ Ρ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΠΌΠΈ, ΠΊΠΎΡΠΎΡΡΠ΅ Π½ΡΠΆΠ½ΠΎ Π²ΡΡΠΈΡΠ»ΡΡΡ ΡΠ°ΠΊ ΠΏΡΠΎΡΡΠΎ Π½Π΅ ΠΏΠΎΠ»ΡΡΠΈΠ»ΠΎΡΡ.
ΠΠΎΡΠ»Π΅ Π΄ΠΎΠ»Π³ΠΎΠ³ΠΎ ΡΠ°Π·ΠΌΡΡΠ»Π΅Π½ΠΈΡ Ρ ΠΏΡΠΈΡΠ΅Π» ΠΊ ΠΊΠ»Π°ΡΡΠΈΡΠ΅ΡΠΊΠΎΠΉ model-view ΡΡ Π΅ΠΌΠ΅.
ΠΠ±ΡΠ΅ΠΊΡΡ-Π½Π°ΡΠ»Π΅Π΄Π½ΠΈΠΊΠΈ screen ΡΠ²Π»ΡΡΡΡΡ Π²ΡΡΡΠΊΠ°ΠΌΠΈ β ΠΎΠ½ΠΈ ΠΎΡΠΎΠ±ΡΠ°ΠΆΠ°ΡΡ ΡΠ°Π·Π»ΠΈΡΠ½ΡΠ΅ Π΄Π°Π½Π½ΡΠ΅ ΠΈΠ· ΠΌΠΎΠ΄Π΅Π»ΠΈ, Π½ΠΎ ΡΠ°ΠΌΠΈ Π΄Π°Π½Π½ΡΠ΅ Π½Π΅ ΠΏΡΠΎΠΈΠ·Π²ΠΎΠ΄ΡΡ. ΠΡΡ Π»ΠΎΠ³ΠΈΠΊΠ° Π»Π΅ΠΆΠΈΡ Π² ΠΊΠ»Π°ΡΡΠ΅ GPSDataModel. ΠΠ½ ΠΎΡΠ²Π΅ΡΠ°Π΅Ρ Π·Π° Ρ ΡΠ°Π½Π΅Π½ΠΈΠ΅ ΠΌΠ³Π½ΠΎΠ²Π΅Π½Π½ΡΡ GPS Π΄Π°Π½Π½ΡΡ (ΠΏΠΎΠΊΠ° Π½Π΅ ΠΏΡΠΈΠ΅Π΄ΡΡ Π½ΠΎΠ²ΡΠ΅ Π΄Π°Π½Π½ΡΠ΅ ΠΈΠ· NeoGPS). Π’Π°ΠΊ ΠΆΠ΅ ΠΎΠ½ ΠΎΡΠ²Π΅ΡΠ°Π΅Ρ Π·Π° Π²ΡΡΠΈΡΠ»Π΅Π½ΠΈΠ΅ Π½ΠΎΠ²ΡΡ Π΄Π°Π½Π½ΡΡ , ΡΠ°ΠΊΠΈΡ ΠΊΠ°ΠΊ ΠΎΠ΄ΠΎΠΌΠ΅ΡΡΡ ΠΈΠ»ΠΈ Π²Π΅ΡΡΠΈΠΊΠ°Π»ΡΠ½Π°Ρ ΡΠΊΠΎΡΠΎΡΡΡ. Π ΠΏΠΎΡΠ»Π΅Π΄Π½Π΅Π΅, Π½ΠΎ Π½Π΅ ΠΌΠ΅Π½Π΅Π΅ Π²Π°ΠΆΠ½ΠΎΠ΅ β ΡΡΠΎΡ ΠΊΠ»Π°ΡΡ ΡΠ°ΠΌ Π·Π°Π½ΠΈΠΌΠ°Π΅ΡΡΡ Π²ΡΠ΅ΠΉ ΡΠΈΠ½Ρ ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΠ΅ΠΉ Π΄Π»Ρ ΡΠ²ΠΎΠΈΡ Π΄Π°Π½Π½ΡΡ .
const uint8 ODOMERTERS_COUNT = 3;
/**
Β * GPS data model. Encapsulates all the knowledge about various GPS related data in the device
Β */
class GPSDataModel
{
public:
GPSDataModel();
void processNewGPSFix(const gps_fix & fix);
void processNewSatellitesData(NMEAGPS::satellite_view_t * sattelites, uint8_t count);
gps_fix getGPSFix() const;
GPSSatellitesData getSattelitesData() const;
float getVerticalSpeed() const;
int timeDifference() const;
// Odometers
GPSOdometerData getOdometerData(uint8 idx) const;
void resumeOdometer(uint8 idx);
void pauseOdometer(uint8 idx);
void resetOdometer(uint8 idx);
void resumeAllOdometers();
void pauseAllOdometers();
void resetAllOdometers();
private:
gps_fix cur_fix; /// most recent fix data
gps_fix prev_fix; /// previously set fix data
GPSSatellitesData sattelitesData; // Sattelites count and signal power
GPSOdometer * odometers[ODOMERTERS_COUNT];
bool odometerWasActive[ODOMERTERS_COUNT];
SemaphoreHandle_t xGPSDataMutex;
GPSDataModel( const GPSDataModel &c );
GPSDataModel& operator=( const GPSDataModel &c );
}; //GPSDataModel
/// A single instance of GPS data model
extern GPSDataModel gpsDataModel;
Π’.ΠΊ. ΠΊΠ»Π°ΡΡ ΠΌΠΎΠ΄Π΅Π»ΠΈ ΠΎΡΠ²Π΅ΡΠ°Π΅Ρ Π·Π° ΡΠΈΠ½Ρ ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΡ Π΄Π°Π½Π½ΡΡ ΠΌΠ΅ΠΆΠ΄Ρ ΠΏΠΎΡΠΎΠΊΠ°ΠΌΠΈ, ΡΠΎ Π² Π½Π΅ΠΌ ΠΆΠΈΠ²Π΅Ρ ΠΌΡΡΠ΅ΠΊΡ, ΠΊΠΎΡΠΎΡΡΠΉ ΡΠ΅Π³ΡΠ»ΠΈΡΡΠ΅Ρ Π΄ΠΎΡΡΡΠΏ ΠΊ Π²Π½ΡΡΡΠ΅Π½Π½ΠΈΠΌ ΠΏΠΎΠ»ΡΠΌ ΠΊΠ»Π°ΡΡΠ°. ΠΠ½Π΅ Π±ΡΠ»ΠΎ ΠΆΡΡΠΊΠΎ Π½Π΅ΡΠ΄ΠΎΠ±Π½ΠΎ (ΠΈ Π½Π΅ΠΊΡΠ°ΡΠΈΠ²ΠΎ) ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡΡΡ Π³ΠΎΠ»ΡΠΌΠΈ xSemaphoreTake()/xSemaphoreGive(), ΡΠ°ΠΊ ΡΡΠΎ Ρ Π½Π°ΡΠΈΡΠΎΠ²Π°Π» ΠΊΠ»Π°ΡΡΠΈΡΠ΅ΡΠΊΠΈΠΉ Π°Π²ΡΠΎΠ·Π°Ρ Π²Π°ΡΡΠ²Π°ΡΠ΅Π»Ρ (ΡΠΎΡΠ½Π΅Π΅ Π΄Π°ΠΆΠ΅ Π°Π²ΡΠΎΠΎΡΠΏΡΡΠΊΠ°ΡΠ΅Π»Ρ).
class MutexLocker
{
public:
MutexLocker(SemaphoreHandle_t mtx)
{
mutex = mtx;
xSemaphoreTake(mutex, portMAX_DELAY);
}
~MutexLocker()
{
xSemaphoreGive(mutex);
}
private:
SemaphoreHandle_t mutex;
};
ΠΠ°Π±ΡΠ°ΡΡ ΡΠ΅ΠΊΡΡΠ΅Π΅ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ ΠΎΡΠ΅Π½Ρ ΠΏΡΠΎΡΡΠΎ. ΠΡΠΆΠ½ΠΎ ΠΏΡΠΎΡΡΠΎ Π²ΡΠ·Π²Π°ΡΡ ΡΡΠ½ΠΊΡΠΈΡ getGPSFix(), ΠΊΠΎΡΠΎΡΠ°Ρ ΠΏΡΠΎΡΡΠΎ Π²Π΅ΡΠ½Π΅Ρ ΠΊΠΎΠΏΠΈΡ Π΄Π°Π½Π½ΡΡ .
gps_fix GPSDataModel::getGPSFix() const
{
MutexLocker lock(xGPSDataMutex);
return cur_fix;
}
ΠΠ»ΠΈΠ΅Π½ΡΡ Π½Π΅ Π½ΡΠΆΠ½ΠΎ ΠΏΠ°ΡΠΈΡΡΡ ΠΏΡΠΎ Π±Π»ΠΎΠΊΠΈΡΠΎΠ²ΠΊΠΈ ΠΈ Π²ΡΠ΅ ΡΠ°ΠΊΠΎΠ΅. ΠΡΠΎΡΡΠΎ Π·Π°Π±ΠΈΡΠ°Π΅ΠΌ Π΄Π°Π½Π½ΡΠ΅ ΠΈ ΡΠΈΡΡΠ΅ΠΌ ΠΊΠ°ΠΊ Π½Π°Π΄ΠΎ.
void SpeedScreen::drawScreen() const
{
// Get the gps fix data
gps_fix gpsFix = gpsDataModel.getGPSFix();
// Draw speed
...
printNumber(buf, gpsFix.speed_kph(), 4, true);
Π ΠΊΠ»Π°ΡΡΠ΅ ΠΌΠΎΠ΄Π΅Π»ΠΈ Ρ ΡΠ°Π½ΠΈΡΡΡ Π½Π΅ ΡΠΎΠ»ΡΠΊΠΎ ΡΠ°ΠΌΡΠ΅ ΠΏΠΎΡΠ»Π΅Π΄Π½ΠΈΠ΅ Π΄Π°Π½Π½ΡΠ΅ (cur_fix), Π½ΠΎ ΡΠ°ΠΊΠΆΠ΅ ΠΏΡΠ΅Π΄ΡΠ΄ΡΡΠ΅Π΅ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ (prev_fix). Π’Π°ΠΊ ΡΡΠΎ, Π²ΡΡΠΈΡΠ»Π΅Π½ΠΈΠ΅ Π²Π΅ΡΡΠΈΠΊΠ°Π»ΡΠ½ΠΎΠΉ ΡΠΊΠΎΡΠΎΡΡΠΈ ΡΡΠ°Π½ΠΎΠ²ΠΈΡΡΡ ΡΡΠΈΠ²ΠΈΠ°Π»ΡΠ½ΠΎΠΉ Π·Π°Π΄Π°ΡΠ΅ΠΉ.
float GPSDataModel::getVerticalSpeed() const
{
MutexLocker lock(xGPSDataMutex);
// Return NAN to indicate vertical speed not available
if(!cur_fix.valid.altitude || !prev_fix.valid.altitude)
return NAN;
return cur_fix.altitude() - prev_fix.altitude(); // Assuming that time difference between cur and prev fix is 1 second
}
Π‘ Π΄Π°Π½Π½ΡΠΌΠΈ ΠΏΡΠΎ ΡΠΏΡΡΠ½ΠΈΠΊΠΈ ΠΏΠΎΠ»ΡΡΠΈΠ»ΠΎΡΡ Π²Π΅ΡΡΠΌΠ° ΠΈΠ½ΡΠ΅ΡΠ΅ΡΠ½ΠΎ. ΠΠ°Π½Π½ΡΠ΅ ΠΏΡΠΎ ΡΠΏΡΡΠ½ΠΈΠΊΠΈ ΠΆΠΈΠ²ΡΡ Π² ΠΌΠ°ΡΡΠΈΠ²Π΅ ΡΡΡΡΠΊΡΡΡ NMEAGPS::satellite_view_t. ΠΠ°ΡΡΠΈΠ² Π²Π΅ΡΠΈΡ 150 Π±Π°ΠΉΡ ΠΈ, ΠΊΠ°ΠΊ Ρ ΡΠΆΠ΅ ΠΏΠΈΡΠ°Π», Π΅Π³ΠΎ Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠΎ Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΎ ΡΠ°Π· ΠΊΠΎΠΏΠΈΡΠΎΠ²Π°ΡΡ. ΠΠ΅ ΡΠ°ΠΊ, ΡΡΠΎΠ±Ρ ΠΊΡΠΈΡΠΈΡΠ½ΠΎ ΠΏΡΠΈ Π½Π°Π»ΠΈΡΠΈΠΈ 20ΠΊΠ± ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠ²Ρ, Π½ΠΎ Π²ΡΠ΅ ΡΠ°Π²Π½ΠΎ ΡΡΠΎ ΡΡΠΈΠΆΠ΄Ρ ΠΏΠΎ 150 Π±Π°ΠΉΡ.
Π ΠΊΠΎΠ½ΡΠ΅ ΠΊΠΎΠ½ΡΠΎΠ² Ρ ΠΏΠΎΠ½ΡΠ», ΡΡΠΎ ΠΌΠ½Π΅ Π½Π΅ Π½ΡΠΆΠ½Ρ Π²ΡΠ΅ Π΄Π°Π½Π½ΡΠ΅, Π΄ΠΎΡΡΠ°ΡΠΎΡΠ½ΠΎ ΡΠΊΠΎΠΏΠΈΡΠΎΠ²Π°ΡΡ ΡΠ΅Π±Π΅ ΡΠΎΠ»ΡΠΊΠΎ ΡΠΎ, ΡΡΠΎ ΡΠ΅Π°Π»ΡΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ. Π ΠΈΡΠΎΠ³Π΅ ΡΠΎΠ΄ΠΈΠ»ΡΡ Π²ΠΎΡ ΡΠ°ΠΊΠΎΠΉ ΠΊΠ»Π°ΡΡ.
class GPSSatellitesData
{
// Partial copy of NMEAGPS::satellite_view_t trimmed to used data
struct SatteliteData
{
uint8_t snr;
bool tracked;
};
SatteliteData satellitesData[SAT_ARRAY_SIZE];
uint8_t sat_count;
public:
GPSSatellitesData();
void parseSatellitesData(NMEAGPS::satellite_view_t * sattelites, uint8_t count);
uint8_t getSattelitesCount() const {return sat_count;}
uint8_t getSatteliteSNR(uint8_t sat) const {return satellitesData[sat].snr;}
bool isSatteliteTracked(uint8_t sat) const {return satellitesData[sat].tracked;}
};
Π’Π°ΠΊΠΎΠΉ ΠΊΠ»Π°ΡΡ ΡΠΆΠ΅ Π½Π΅ ΡΠ°ΠΊ ΠΎΠ±ΠΈΠ΄Π½ΠΎ Π»ΠΈΡΠ½ΠΈΠΉ ΡΠ°Π· ΠΊΠΎΠΏΠΈΡΠΎΠ²Π°ΡΡ β ΠΎΠ½ Π·Π°Π½ΠΈΠΌΠ°Π΅Ρ Π²ΡΠ΅Π³ΠΎ 40 Π±Π°ΠΉΡ.
Π‘Π°ΠΌΠΎΠΉ ΡΠ»ΠΎΠΆΠ½ΠΎΠΉ ΡΠ°ΡΡΡΡ ΡΡ Π΅ΠΌΡ ΠΏΠΎΠ»ΡΡΠΈΠ»ΡΡ ΠΊΠ»Π°ΡΡ GPSOdometer. ΠΠ°ΠΊ ΡΠ»Π΅Π΄ΡΠ΅Ρ ΠΈΠ· Π½Π°Π·Π²Π°Π½ΠΈΡ ΠΎΠ½ ΠΎΡΠ²Π΅ΡΠ°Π΅Ρ Π·Π° Π²ΡΠ΅ Π²ΡΡΠΈΡΠ»Π΅Π½ΠΈΡ ΡΠ²ΡΠ·Π°Π½Π½ΡΠ΅ Ρ ΡΡΠ½ΠΊΡΠΈΠΎΠ½Π°Π»ΡΠ½ΠΎΡΡΡΡ ΠΎΠ΄ΠΎΠΌΠ΅ΡΡΠ°.
// This class represents a single odometer data with no logic around
class GPSOdometerData
{
// GPSOdometer and its data are basically a single object. The difference is only that data can be easily copied
// while GPS odometer object is not supposed to. Additionally access to Odometer object is protected with a mutex
// in the model object
// In order not to overcomplicte design I am allowing GPS Odometer to operate its data members directly.
friend class GPSOdometer;
bool active;
NeoGPS::Location_t startLocation;
NeoGPS::Location_t lastLocation;
float odometer;
int16 startAltitude;
int16 curAltitude;
clock_t startTime; ///! When odometer was turned on for the first time
clock_t sessionStartTime; ///! When odometer was resumed for the current session
clock_t totalTime; ///! Total time for the odometer (difference between now and startTime)
clock_t activeTime; ///! Duration of the current session (difference between now and sessionStartTime)
clock_t activeTimeAccumulator; ///! Sum of all active session duration (not including current one)
float maxSpeed;
public:
GPSOdometerData();
void reset();
// getters
bool isActive() const {return active;}
float getOdometerValue() const {return odometer;}
int16 getAltitudeDifference() const {return (curAltitude - startAltitude) / 100.;} // altitude is in cm
clock_t getTotalTime() const {return totalTime;}
clock_t getActiveTime() const {return activeTimeAccumulator + activeTime;}
float getMaxSpeed() const {return maxSpeed;}
float getAvgSpeed() const;
float getDirectDistance() const;
};
// This is an active odometer object that operates on its odometer data
class GPSOdometer
{
GPSOdometerData data;
public:
GPSOdometer();
// odometer control
void processNewFix(const gps_fix & fix);
void startOdometer();
void pauseOdometer();
void resetOdometer();
// Some data getters
GPSOdometerData getData() {return data;}
bool isActive() const {return data.isActive();}
}; //GPSOdometer
Π‘Π»ΠΎΠΆΠ½ΠΎΡΡΡ Π²ΠΎΡ Π² ΡΠ΅ΠΌ. ΠΠ±ΡΠ΅ΠΊΡ gps_fix, ΠΊΠΎΡΠΎΡΡΠΉ ΠΏΠΎΡΡΡΠΏΠ°Π΅Ρ Π½Π°ΠΌ ΠΎΡ GPS, ΠΌΠΎΠΆΠ΅Ρ ΡΠΎΠ΄Π΅ΡΠΆΠ°ΡΡ ΠΊΠ°ΠΊΠΈΠ΅ ΡΠΎ Π΄Π°Π½Π½ΡΠ΅, Π° ΠΊΠ°ΠΊΠΈΠ΅ ΡΠΎ ΠΌΠΎΠΆΠ΅Ρ ΠΈ Π½Π΅Ρ. ΠΠ°ΠΏΡΠΈΠΌΠ΅Ρ ΠΊΠΎΠΎΡΠ΄ΠΈΠ½Π°ΡΠ° ΠΏΡΠΈΠ΅Π΄Π΅Ρ, Π° Π²ΡΡΠΎΡΠ° Π½Π΅Ρ. Π Π² ΡΠ»Π΅Π΄ΡΡΡΠ΅ΠΌ ΡΠΈΠΊΡΠ΅ ΠΌΠΎΠΆΠ΅Ρ Π±ΡΡΡ Π½Π°ΠΎΠ±ΠΎΡΠΎΡ. ΠΠΎΡΡΠΎΠΌΡ ΠΏΡΠΎΡΡΠΎ ΡΠΎΡ ΡΠ°Π½ΡΡΡ gps_fix Π½Π΅ Π²ΡΠΉΠ΄Π΅Ρ. ΠΡΠΆΠ½ΠΎ ΠΊΠ°ΠΆΠ΄ΡΠΉ ΡΠ°Π· ΡΠΌΠΎΡΡΠ΅ΡΡ ΡΡΠΎ Π΄ΠΎΡΡΡΠΏΠ½ΠΎ Π² Π½ΠΎΠ²ΠΎΠΌ ΡΠΈΠΊΡΠ΅, Π° ΡΡΠΎ Π½Π΅Ρ. ΠΠΎΡΡΠΎΠΌΡ ΠΏΡΠΈΡΠ»ΠΎΡΡ Π³ΠΎΡΠΎΠ΄ΠΈΡΡ Π²Π΅ΡΡΠΌΠ° ΡΠ»ΠΎΠΆΠ½ΡΠΉ Π°Π»Π³ΠΎΡΠΈΡΠΌ, Π·Π°ΠΏΠΎΠΌΠΈΠ½Π°ΡΡ ΠΊΠΎΠΎΡΠ΄ΠΈΠ½Π°ΡΡ, Π²ΡΡΠΎΡΡ ΠΈ Π²ΡΠ΅ΠΌΠ΅Π½Π½ΡΠ΅ ΠΎΡΠΌΠ΅ΡΠΊΠΈ ΠΏΠΎ ΠΎΡΠ΄Π΅Π»ΡΠ½ΠΎΡΡΠΈ.
void GPSOdometer::processNewFix(const gps_fix & fix)
{
Serial.print("GPSOdometer: Processing new fix ");
Serial.println((int32)this);
if(data.active)
{
Serial.println("Active odometer: Processing new fix");
// Fill starting position if needed
if(fix.valid.location && !isValid(data.startLocation))
data.startLocation = fix.location;
// Fill starting altitude if neede
if(fix.valid.altitude && !data.startAltitude) // I know altitude can be zero, but real zero cm altutude would be very rare condition. Hope this is not a big deal
data.startAltitude = fix.altitude_cm();
// Fill starting times if needed
if(fix.valid.time)
{
if(!data.startTime)
data.startTime = fix.dateTime;
if(!data.sessionStartTime)
data.sessionStartTime = fix.dateTime;
}
// Increment the odometer
if(fix.valid.location)
{
// but only if previous location is really valid
if(isValid(data.lastLocation))
data.odometer += NeoGPS::Location_t::DistanceKm(fix.location, data.lastLocation);
// In any case store current (valid) fix
data.lastLocation = fix.location;
}
// Store current altitude
if(fix.valid.altitude)
data.curAltitude = fix.altitude_cm();
// update active time values
if(fix.valid.time)
data.activeTime = fix.dateTime - data.sessionStartTime;
// update max speed value
if(fix.valid.speed && fix.speed_kph() > data.maxSpeed)
data.maxSpeed = fix.speed_kph();
}
//Total time can be updated regardless of active state
if(fix.valid.time && data.startTime)
data.totalTime = fix.dateTime - data.startTime;
}
Π ΡΡΠΎΠΌ ΠΌΠ΅ΡΡΠ΅ Ρ ΠΌΠ΅Π½Ρ ΡΠ΅Π·ΠΊΠΎ ΡΠ²Π΅Π»ΠΈΡΠΈΠ»ΡΡ ΡΠ°Π·ΠΌΠ΅Ρ ΡΠ»Π΅ΡΠ° β ΠΏΠΎΡΡΠΈ Π½Π° 10ΠΊΠ±. Π ΠΏΡΠΎΠ΅ΠΊΡ ΠΏΡΠΈΠΏΠΎΠ»Π·Π»Π° ΠΊΡΡΠ° ΠΌΠ°ΡΠ΅ΠΌΠ°ΡΠΈΡΠ΅ΡΠΊΠΎΠ³ΠΎ ΠΊΠΎΠ΄Π° β ΡΠΈΠ½ΡΡΡ, ΠΊΠΎΡΠΈΠ½ΡΡΡ, ΡΠ°Π½Π³Π΅Π½ΡΡ, ΠΊΠ²Π°Π΄ΡΠ°ΡΠ½ΡΠ΅ ΠΊΠΎΡΠ½ΠΈ ΠΈ Π²ΡΠ΅ ΡΠ°ΠΊΠΎΠ΅ ΠΏΡΠΎΡΠ΅Π΅. ΠΠΊΠ°Π·Π°Π»ΠΎΡΡ, ΡΡΠΎ Π½ΠΎΠ³ΠΈ ΡΠ°ΡΡΡΡ ΠΈΠ· ΡΡΠ½ΠΊΡΠΈΠΈ NeoGPS::Location_t::DistanceKm() β Π²ΡΠ΅ ΡΡΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ Π² Π²ΡΡΠΈΡΠ»Π΅Π½ΠΈΠΈ ΡΠ°ΡΡΡΠΎΡΠ½ΠΈΡ Π½Π° ΠΎΡΠ½ΠΎΠ²Π΅ ΠΊΠΎΠΎΡΠ΄ΠΈΠ½Π°Ρ. Π‘ΠΊΡΠΈΠΏΡ Π·ΡΠ±Π°ΠΌΠΈ ΠΏΡΠΈΡΠ»ΠΎΡΡ ΡΠΎΠ³Π»Π°ΡΠΈΡΡΡ, Π½ΠΎ Π·Π°Π΄ΡΠΌΠ°Π»ΡΡ ΠΎ ΠΊΠΎΠ½ΡΡΠΎΠ»Π»Π΅ΡΠ΅ Π½Π° Cortex M4 β ΡΠ°ΠΌ ΡΡΠΎ Ρ Π°ΡΠ΄Π²Π°ΡΠ½ΠΎ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π²ΡΡΠΈΡΠ»ΡΡΡΡΡ.
void GPSOdometer::startOdometer()
{
data.active = true;
// Reset session values
data.sessionStartTime = 0;
data.activeTime = 0;
}
void GPSOdometer::pauseOdometer()
{
data.active = false;
data.activeTimeAccumulator += data.activeTime;
data.activeTime = 0;
}
void GPSOdometer::resetOdometer()
{
data.reset();
}
ΠΠ±ΡΠ°ΡΠΈΡΠ΅ Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅, ΡΡΠΎ Π² ΠΊΠ»Π°ΡΡΠ΅ ΠΎΠ΄ΠΎΠΌΠ΅ΡΡΠ° Π½Π΅Ρ Π½ΠΈΠΊΠ°ΠΊΠΎΠΉ ΡΠΈΠ½Ρ ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΠΈ. ΠΡΠΎ ΠΏΠΎΡΠΎΠΌΡ, ΡΡΠΎ Π²ΡΡ ΡΠΈΠ½Ρ ΡΠΎΠ½ΠΈΠ·Π°ΡΠΈΡ ΠΏΡΠΎΠΈΡΡ ΠΎΠ΄ΠΈΡ Π² ΠΊΠ»Π°ΡΡΠ΅ GPSDataModel. Π― ΠΏΡΠΎΡΡΠΎ Π½Π΅ Ρ ΠΎΡΠ΅Π» Π³ΠΎΡΠΎΠ΄ΠΈΡΡ ΠΏΠΎ ΠΌΡΡΠ΅ΠΊΡΡ Π² ΠΊΠ°ΠΆΠ΄ΠΎΠΌ ΠΎΠ±ΡΠ΅ΠΊΡΠ΅. ΠΠΎ ΠΈΠ·-Π·Π° ΡΡΠΎΠ³ΠΎ ΠΌΠ½Π΅ ΠΏΡΠΈΡΠ»ΠΎΡΡ ΡΡΠ»ΠΎΠΆΠ½ΠΈΡΡ ΡΠ°ΠΌ ΠΊΠ»Π°ΡΡ ΠΎΠ΄ΠΎΠΌΠ΅ΡΡΠ° ΠΈ ΡΠ°Π·Π΄Π΅Π»ΠΈΡΡ Π½Π° 2 ΠΊΠ»Π°ΡΡΠ°: ΠΎΠ±ΡΠ΅ΠΊΡ Ρ Π΄Π°Π½Π½ΡΠΌΠΈ (GPSOdometerData) ΠΌΠΎΠΆΠ΅Ρ ΠΊΠΎΠΏΠΈΡΠΎΠ²Π°ΡΡΡΡ ΠΏΠΎ Π·Π°ΠΏΡΠΎΡΡ ΠΊΠ»ΠΈΠ΅Π½ΡΠΎΠ², ΡΠΎΠ³Π΄Π° ΠΊΠ°ΠΊ ΠΎΠ±ΡΠ΅ΠΊΡ ΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΡ (GPSOdometer) ΡΠΎΠ·Π΄Π°ΡΡΡΡ ΠΎΠ΄ΠΈΠ½ ΡΠ°Π· Π½Π° ΠΊΠ°ΠΆΠ΄ΡΠΉ ΠΎΠ΄ΠΎΠΌΠ΅ΡΡ. ΠΠ·-Π·Π° ΡΡΠΎΠ³ΠΎ ΡΠ°ΠΊΠΆΠ΅ ΠΏΡΠΈΡΠ»ΠΎΡΡ ΠΎΠ΄ΠΈΠ½ ΠΊΠ»Π°ΡΡ ΡΠ΄Π΅Π»Π°ΡΡ friendβΠΎΠΌ Π΄ΡΡΠ³ΠΎΠΌΡ. ΠΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ Ρ ΠΏΠ΅ΡΠ΅ΡΠΌΠΎΡΡΡ ΡΡΠΎΡ Π΄ΠΈΠ·Π°ΠΉΠ½ Π² Π±ΡΠ΄ΡΡΠ΅ΠΌ.
Π’Π°ΠΊ Π²ΡΠ³Π»ΡΠ΄ΠΈΡ ΠΎΡΠ½ΠΎΠ²Π½ΠΎΠΉ ΡΠΊΡΠ°Π½ ΠΎΠ΄ΠΎΠΌΠ΅ΡΡΠ°. Π‘ΠΈΠΌΠ²ΠΎΠ» ΡΠΎΡΠΊΠΈ Π² ΡΡΠΈΡΡ Π΅ΡΠ΅ Π½Π΅ Π΄ΠΎΠ±Π°Π²ΠΈΠ» β Π΄ΠΎΠ»ΠΆΠ½ΠΎ ΠΏΠΎΠΊΠ°Π·ΡΠ²Π°ΡΡ 0.42ΠΊΠΌ. Π’Π°ΠΊ ΠΆΠ΅ ΠΎΡΠΎΠ±ΡΠ°ΠΆΠ°Π΅ΡΡΡ ΠΏΠ΅ΡΠ΅ΠΏΠ°Π΄ Π²ΡΡΠΎΡ β Π»Π΅ΠΆΠ° Π½Π° ΠΌΠ΅ΡΡΠ΅ Π½Π° ΠΏΠΎΠ΄ΠΎΠΊΠΎΠ½Π½ΠΈΠΊΠ΅ Π·Π°ΠΏΡΠΎΡΡΠΎ ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠ΅ΡΠ΅ΠΏΠ°ΡΡΡ Π½Π° 18 ΠΈ Π±ΠΎΠ»Π΅Π΅ ΠΌΠ΅ΡΡΠΎΠ².
ΠΡΡΠ³ΠΈΠ΅ ΠΏΠΎΠ»Π΅Π·Π½ΡΠ΅ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΡ, ΠΊΠΎΡΠΎΡΡΠ΅ ΠΌΠΎΠ³ΡΡ ΠΎΡΠΎΠ±ΡΠ°ΠΆΠ°ΡΡΡΡ ΠΎΠ΄ΠΎΠΌΠ΅ΡΡΠΎΠΌ. ΠΠ° ΠΎΠ΄ΠΈΠ½ ΡΠΊΡΠ°Π½ Π²ΡΠ΅ Π΄Π°ΠΆΠ΅ Π½Π΅ Π²ΠΌΠ΅ΡΡΠΈΠ»ΠΎΡΡ β Π±ΡΠ΄Ρ Π΄Π΅Π»Π°ΡΡ 2 ΠΈΠ»ΠΈ Π΄Π°ΠΆΠ΅ 3 ΡΠΊΡΠ°Π½Π°.
GPSDataModel ΠΌΠΎΠΆΠ΅Ρ ΡΠΏΡΠ°Π²Π»ΡΡΡ Π²ΡΠ΅ΠΌΠΈ ΠΎΠ΄ΠΎΠΌΠ΅ΡΡΠ°ΠΌΠΈ ΡΡΠ°Π·Ρ. ΠΡΠ° ΡΠΈΡΠ° Π±ΡΠ»Π° ΠΏΡΠ΅Π΄Π»ΠΎΠΆΠ΅Π½Π° Π² ΠΊΠΎΠΌΠ΅Π½ΡΠ°ΡΠΈΡΡ ΠΈ Π΄ΠΎΠ»ΠΆΠ½Π° Π±ΡΡΡ ΡΠ΄ΠΎΠ±Π½ΠΎΠΉ β Π·Π°ΡΠ΅Π» Π² ΠΊΠ°ΡΠ΅, Π²ΡΠΊΠ»ΡΡΠΈΠ» Π²ΡΠ΅ ΠΎΠ΄ΠΎΠΌΠ΅ΡΡΡ ΡΡΠ°Π·Ρ. ΠΡΡΠ΅Π» β Π²ΠΊΠ»ΡΡΠΈΠ» ΠΈΡ ΠΎΠΏΡΡΡ.
void GPSDataModel::resumeAllOdometers()
{
MutexLocker lock(xGPSDataMutex);
if(odometerWasActive[0])
odometers[0]->startOdometer();
if(odometerWasActive[1])
odometers[1]->startOdometer();
if(odometerWasActive[2])
odometers[2]->startOdometer();
}
void GPSDataModel::pauseAllOdometers()
{
MutexLocker lock(xGPSDataMutex);
odometerWasActive[0] = odometers[0]->isActive();
odometerWasActive[1] = odometers[1]->isActive();
odometerWasActive[2] = odometers[2]->isActive();
odometers[0]->pauseOdometer();
odometers[1]->pauseOdometer();
odometers[2]->pauseOdometer();
}
void GPSDataModel::resetAllOdometers()
{
MutexLocker lock(xGPSDataMutex);
odometers[0]->resetOdometer();
odometers[1]->resetOdometer();
odometers[2]->resetOdometer();
odometerWasActive[0] = false;
odometerWasActive[1] = false;
odometerWasActive[2] = false;
}
ΠΠΏΡΡΡ FreeRTOSβΠΈΠΌ
Π ΡΠ΅Π»ΡΡ ΠΈΠ·ΡΡΠ΅Π½ΠΈΡ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΠ΅ΠΉ FreeRTOS Ρ ΠΏΠΎΠΏΡΠΎΠ±ΠΎΠ²Π°Π» ΠΏΠΎΡΠΌΠΎΡΡΠ΅ΡΡ ΡΠΊΠΎΠ»ΡΠΊΠΎ ΠΆΠ΅ Π½Π° ΡΠ°ΠΌΠΎΠΌ Π΄Π΅Π»Π΅ Π²ΡΠ΅ΠΌΠ΅Π½ΠΈ ΠΏΡΠΎΡΠ΅ΡΡΠΎΡ ΠΏΡΠΎΠ²ΠΎΠ΄ΠΈΡ Π² Π²ΡΡΠΈΡΠ»Π΅Π½ΠΈΡΡ . ΠΠ»Ρ ΠΎΡΠ΅Π½ΠΊΠΈ ΠΌΠΎΠΆΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ApplicationIdleHook.
Π£ Π»ΡΠ±ΠΎΠΉ Π Π’ΠΠ‘ Π΅ΡΡΡ ΡΠ°ΠΊ Π½Π°Π·ΡΠ²Π°Π΅ΠΌΡΠΉ idle ΠΏΠΎΡΠΎΠΊ. ΠΡΠ»ΠΈ ΠΏΡΠΎΡΠ΅ΡΡΠΎΡΡ Π½Π΅ΡΠ΅ΠΌ ΡΠ΅Π±Ρ Π·Π°Π½ΡΡΡ β ΠΊΡΡΡΠΈΡΡΡ Π½Π΅ΠΊΠΈΠΉ Π±Π΅ΡΠΊΠΎΠ½Π΅ΡΠ½ΡΠΉ ΡΠΈΠΊΠ» Π² ΠΎΡΠ΄Π΅Π»ΡΠ½ΠΎΠΉ Π·Π°Π΄Π°ΡΠ΅ Ρ Π½Π°ΠΈΠΌΠ΅Π½ΡΡΠΈΠΌ ΠΏΡΠΈΠΎΡΠΈΡΠ΅ΡΠΎΠΌ. FreeRTOS ΠΏΠΎΠ·Π²ΠΎΠ»ΡΠ΅Ρ Π΄ΠΎΠ±Π°Π²ΠΈΡΡ Π½Π΅ΠΊΠΎΡΠΎΡΠΎΠΉ ΠΏΠΎΠ»Π΅Π·Π½ΠΎΡΡΠΈ Π² ΡΡΠΎΡ Π±Π΅ΡΠΊΠΎΠ½Π΅ΡΠ½ΡΠΉ ΡΠΈΠΊΠ» ΠΈ Π·Π°ΠΏΡΡΠΊΠ°ΡΡ ΡΡΠΎΡ Ρ ΡΠΊ. ΠΠ΄Π΅Ρ ΠΈΠ·ΠΌΠ΅ΡΠ΅Π½ΠΈΡ Π·Π°Π³ΡΡΠ·ΠΊΠΈ ΠΏΡΠΎΡΠ΅ΡΡΠΎΡΠ° ΡΠΎΡΡΠΎΠΈΡ Π² ΡΠΎΠΌ, ΡΡΠΎ ΡΠ΅ΠΌ Π±ΠΎΠ»ΡΡΠ΅ ΠΏΡΠΎΡΠ΅ΡΡΠΎΡ ΠΏΡΠΎΠ²ΠΎΠ΄ΠΈΡ Π²ΡΠ΅ΠΌΠ΅Π½ΠΈ Π² idle ΠΏΠΎΡΠΎΠΊΠ΅ β ΡΠ΅ΠΌ ΠΌΠ΅Π½ΡΡΠ΅ ΠΎΠ½ Π·Π°Π³ΡΡΠΆΠ΅Π½ Π΄ΡΡΠ³ΠΎΠΉ (ΠΏΠΎΠ»Π΅Π·Π½ΠΎΠΉ) ΡΠ°Π±ΠΎΡΠΎΠΉ.
Π ΠΈΠ½ΡΠ΅ΡΠ½Π΅ΡΠ°Ρ Ρ Π½Π°ΡΠ΅Π» Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΎ ΠΏΠΎΠ΄Ρ ΠΎΠ΄ΠΎΠ² ΠΊΠ°ΠΊ ΠΌΠΎΠΆΠ½ΠΎ Π±ΡΠ»ΠΎ Π±Ρ ΠΈΠ·ΠΌΠ΅ΡΡΡΡ Π·Π°Π³ΡΡΠ·ΠΊΡ ΠΏΡΠΎΡΠ΅ΡΡΠΎΡΠ°.
ΠΠ΄Π½ΠΈ ΡΠ΅Π±ΡΡΠ° ΠΏΡΠ΅Π΄Π»Π°Π³Π°Π»ΠΈ Π² Idle Hook ΡΡΠ½ΠΊΡΠΈΠΈ ΠΊΡΡΡΠΈΡΡ Π½Π΅ΠΊΠΈΠΉ ΡΡΠ΅ΡΡΠΈΠΊ ΠΈ ΠΈΠ·ΠΌΠ΅ΡΡΡΡ ΡΠΊΠΎΡΠΎΡΡΡ Ρ ΠΊΠΎΡΠΎΡΠΎΠΉ ΠΎΠ½ Β«Π½Π°ΠΌΠ°ΡΡΠ²Π°Π΅ΡΒ». Π§ΡΠΎΠ±Ρ ΠΏΠ΅ΡΠ΅Π²Π΅ΡΡΠΈ ΡΡΠΎ Π² ΠΏΡΠΎΡΠ΅Π½ΡΡ Π½ΡΠΆΠ½ΠΎ ΠΏΠΎΠ»ΡΡΠ΅Π½Π½ΡΡ ΡΠΊΠΎΡΠΎΡΡΡ ΠΏΠΎΠ΄Π΅Π»ΠΈΡΡ Π½Π° Π½Π΅ΠΊΠΈΠ΅ ΡΡΠ°Π»ΠΎΠ½Π½ΠΎΠ΅ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅.
ΠΠΎ Π³Π΄Π΅ Π²Π·ΡΡΡ ΡΡΡ ΡΡΠ°Π»ΠΎΠ½Π½ΡΡ ΡΠΊΠΎΡΠΎΡΡΡ? ΠΠ»Ρ ΡΡΠΎΠ³ΠΎ Π½ΡΠΆΠ½ΠΎ ΠΏΠΎΠ³Π°ΡΠΈΡΡ Π²ΡΠ΅ Π΄ΡΡΠ³ΠΈΠ΅ ΠΏΠΎΡΠΎΠΊΠΈ ΠΈ ΠΌΠ΅ΡΡΡΡ ΡΠΎΠ»ΡΠΊΠΎ ΡΠΊΠΎΡΠΎΡΡΡ ΡΡΠ΅ΡΡΠΈΠΊΠ° Π² Π½Π΅Π½Π°Π³ΡΡΠΆΠ΅Π½Π½ΠΎΠΉ ΡΠΈΡΡΠ΅ΠΌΠ΅. ΠΠΎΠΆΠ½ΠΎ, Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ, Π½Π° ΡΡΠ°ΡΡΠ΅ ΡΠ΄Π΅Π»Π°ΡΡ Π·Π°Π΄Π΅ΡΠΆΠΊΡ Π² 1-2 ΡΠ΅ΠΊΡΠ½Π΄Ρ Π΄Π»Ρ ΠΈΠ·ΠΌΠ΅ΡΠ΅Π½ΠΈΠΉ, Π½ΠΎ Π»ΠΈΡΠ½ΠΎ ΠΌΠ΅Π½Ρ ΠΆΡΡΠΊΠΎ Π±Π΅ΡΠΈΡ ΠΊΠΎΠ³Π΄Π° Π΄ΠΎΡΡΠ°ΡΠΎΡΠ½ΠΎ ΠΏΡΠΎΡΡΡΠ΅ ΡΡΡΡΠΎΠΉΡΡΠ²Π° Β«Π³ΡΡΠ·ΡΡΡΡΒ» ΠΏΠΎ 5-10 ΡΠ΅ΠΊΡΠ½Π΄ (Π½Π°ΠΏΡΠΈΠΌΠ΅Ρ, ΡΠΎΡΠΎΠ°ΠΏΠΏΠ°ΡΠ°ΡΡ-ΠΌΡΠ»ΡΠ½ΠΈΡΡ. Π³ΡΡΡΡ).
Π Π΄ΡΡΠ³ΠΎΠΌ Π²Π°ΡΠΈΠ°Π½ΡΠ΅ ΠΈΠ· ΠΈΠ½ΡΠ΅ΡΠ½Π΅ΡΠ° ΠΏΡΠ΅Π΄Π»Π°Π³Π°Π»ΠΎΡΡ Π·Π°ΠΏΡΡΡΠΈΡΡ ΠΎΡΠ΄Π΅Π»ΡΠ½ΡΠΉ ΡΠ°ΠΉΠΌΠ΅Ρ ΠΈ ΠΏΠ΅ΡΠ΅Π³ΡΡΠ·ΠΈΡΡ ΠΌΠ°ΠΊΡΠΎΡ Π²Ρ ΠΎΠ΄Π° ΠΈ Π²ΡΡ ΠΎΠ΄Π° Π² ΠΊΠΎΠ½ΡΠ΅ΠΊΡΡ ΠΏΠΎΡΠΎΠΊΠ°. ΠΠ΄Π΅Ρ Π² ΡΠΎΠΌ, ΡΡΠΎΠ±Ρ ΠΈΠ·ΠΌΠ΅ΡΡΡΡ ΡΠ°Π·Π½ΠΈΡΡ Π·Π½Π°ΡΠ΅Π½ΠΈΠΉ ΡΠ°ΠΉΠΌΠ΅ΡΠ° Π½Π° Π²Ρ ΠΎΠ΄Π΅ ΠΈ Π½Π° Π²ΡΡ ΠΎΠ΄Π΅ Π² ΠΊΠ°ΠΆΠ΄ΡΠΉ ΠΏΠΎΡΠΎΠΊ ΠΈ ΠΈΠ· ΡΡΠΎΠ³ΠΎ Π΄Π΅Π»Π°ΡΡ Π²ΡΠ²ΠΎΠ΄ ΠΎ Π·Π°Π³ΡΡΠ·ΠΊΠ΅ ΠΏΡΠΎΡΠ΅ΡΡΠΎΡΠ°.
ΠΠ°, Ρ ΡΠ»ΡΡΠ°Π» ΠΎ Run Time Stats Π² ΡΠΈΡΡΠ΅ΠΌΠ΅ FreeRTOS. ΠΠΎ, ΠΊΠ°ΠΊ Π³Π»Π°ΡΠΈΡ ΠΈΠ½ΡΡΡΡΠΊΡΠΈΡ, ΠΎΠ½ΠΎ ΠΏΡΠ΅Π΄Π½Π°Π·Π½Π°ΡΠ΅Π½ΠΎ Π΄Π»Ρ Π΄ΡΡΠ³ΠΎΠ³ΠΎ. ΠΡΠ° ΡΡΠ½ΠΊΡΠΈΡ ΠΏΠΎΠ·Π²ΠΎΠ»ΡΠ΅Ρ Π² Π΄Π΅Π±Π°ΠΆΠ½ΡΡ ΡΠ΅Π»ΡΡ ΠΏΠΎΠ»ΡΡΠΈΡΡ Π·Π°Π³ΡΡΠ·ΠΊΡ ΠΏΠΎ ΠΊΠ°ΠΆΠ΄ΠΎΠΌΡ ΠΎΡΠ΄Π΅Π»ΡΠ½ΠΎΠΌΡ ΠΏΠΎΡΠΎΠΊΡ ΠΈ Π·Π° Π²Π΅ΡΡ ΠΏΠ΅ΡΠΈΠΎΠ΄ ΡΠ°Π±ΠΎΡΡ ΠΏΡΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΡ. Π― ΠΆΠ΅ Ρ ΠΎΡΠ΅Π» ΠΈΠ·ΠΌΠ΅ΡΡΡΡ ΠΌΠ³Π½ΠΎΠ²Π΅Π½Π½ΡΡ Π·Π°Π³ΡΡΠ·ΠΊΡ ΠΏΡΠΎΡΠ΅ΡΡΠΎΡΠ°.
Π― ΡΠ΅ΡΠΈΠ» ΠΏΠΎΠΏΡΠΎΠ±ΠΎΠ²Π°ΡΡ ΡΠ΄Π΅Π»Π°ΡΡ ΡΠ»Π΅Π΄ΡΡΡΠΈΠΉ Π²Π°ΡΠΈΠ°Π½Ρ. ΠΠ΅ Π·Π½Π°Ρ Π½Π°ΡΠΊΠΎΠ»ΡΠΊΠΎ ΡΡΠΎ ΠΏΡΠ°Π²ΠΈΠ»ΡΠ½ΠΎ ΠΈ Π±ΡΠ΄Π΅Ρ Π»ΠΈ ΠΎΠ½ΠΎ Π²ΠΎΠΎΠ±ΡΠ΅ ΡΠ°Π±ΠΎΡΠ°ΡΡ, ΠΊΠΎΠ³Π΄Π° Ρ ΠΏΡΠΈΠΊΡΡΡΡ sleep mode. ΠΠΎ Π½Π° Π΄Π°Π½Π½ΠΎΠΌ ΡΡΠ°ΠΏΠ΅ ΡΠ°Π±ΠΎΡΠ°Π΅Ρ Π½Π΅ΠΏΠ»ΠΎΡ ΠΎ.
static const uint8 periodLen = 9; // 2^periodLen ticks - 512 x 1ms ticks
Β
volatile TickType_t curIdleTicks = 0;
volatile TickType_t lastCountedTick = 0;
volatile TickType_t lastCountedPeriod = 0;
volatile TickType_t lastPeriodIdleValue = 0;
volatile TickType_t minIdleValue = 1 << periodLen;
extern "C" void vApplicationIdleHook( void )
{
// Process idle tick counter
volatile TickType_t curTick = xTaskGetTickCount();
if(curTick != lastCountedTick)
{
curIdleTicks++;
lastCountedTick = curTick;
}
// Store idle metrics each ~0.5 seconds (512 ticks)
curTick >>= periodLen;
Β Β Β if(curTick > lastCountedPeriod)
Β Β Β {
Β Β Β Β Β Β Β Β Β lastPeriodIdleValue = curIdleTicks;
Β Β Β Β Β Β Β Β Β curIdleTicks = 0;
Β Β Β lastCountedPeriod = curTick;
Β Β Β Β Β Β Β Β Β
Β Β Β Β Β Β Β Β Β // Store the max value
Β Β Β Β Β Β Β Β Β if(lastPeriodIdleValue < minIdleValue)
Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β minIdleValue = lastPeriodIdleValue;
Β Β Β }
}
Π€ΡΠ½ΠΊΡΠΈΡ ΠΌΠΎΠΆΠ΅Ρ Π²ΡΠ·ΡΠ²Π°ΡΡΡΡ ΠΎΡΠ΅Π½Ρ ΡΠ°ΡΡΠΎ, ΠΌΠ½ΠΎΠ³ΠΎ ΡΠ°Π· Π·Π° ΠΎΠ΄ΠΈΠ½ ΡΠΈΠΊ ΡΠΈΡΡΠ΅ΠΌΡ (system tick ΡΡΠΎ 1ΠΌΡ). ΠΠΎΡΡΠΎΠΌΡ ΠΏΠ΅ΡΠ²ΡΠΉ Π±Π»ΠΎΠΊ ΠΎΡΠ²Π΅ΡΠ°Π΅Ρ Π·Π° ΠΏΠΎΠ΄ΡΡΠ΅Ρ ΡΠΈΠΊΠΎΠ² (Π° Π½Π΅ Π²ΡΠ·ΠΎΠ²ΠΎΠ²) Π² ΠΊΠΎΡΠΎΡΡΡ Π²ΡΠ·ΡΠ²Π°Π»ΡΡ Ρ ΡΠΊ. ΠΡΠΎΡΠΎΠΉ Π±Π»ΠΎΠΊ ΡΠΎΡ ΡΠ°Π½ΡΠ΅Ρ ΡΡΠ΅ΡΡΠΈΠΊ ΠΊΠ°ΠΆΠ΄ΡΠ΅ 512 ΡΠΈΡΡΠ΅ΠΌΠ½ΡΡ ΡΠΈΠΊΠΎΠ².
ΠΠ°Π³ΡΡΠ·ΠΊΠ° ΠΏΡΠΎΡΠ΅ΡΡΠΎΡΠ° ΡΡΠΎ ΠΎΡΠ½ΠΎΡΠ΅Π½ΠΈΠ΅ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²Π° Π½Π΅-idle ΡΠΈΠΊΠΎΠ² ΠΊ ΠΎΠ±ΡΠ΅ΠΌΡ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²Ρ ΡΠΈΠΊΠΎΠ² Π² ΠΈΠ·ΠΌΠ΅ΡΡΠ΅ΠΌΠΎΠΌ ΠΈΠ½ΡΠ΅ΡΠ²Π°Π»Π΅.
float getCPULoad()
{
Β Β Β return 100. - 100. * lastPeriodIdleValue / (1 << periodLen);
}
Β
float getMaxCPULoad()
{
Β Β Β return 100. - 100. * minIdleValue / (1 << periodLen);
}
ΠΠ°, ΡΡΠΎ ΠΌΠΎΠΆΠ΅Ρ Π±ΡΡΡ Π½Π΅ ΡΠΎΠ²ΡΠ΅ΠΌ ΡΠΎΡΠ½ΠΎ. ΠΠΎ Π² ΡΠ΅Π»ΠΎΠΌ Π΄Π»Ρ ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΡ Π½Π΅ΠΊΠΎΠΉ Π³ΡΡΠ±ΠΎΠΉ ΠΎΡΠ΅Π½ΠΊΠΈ ΠΏΠΎ Π·Π°Π³ΡΡΠ·ΠΊΠ΅ ΡΠΈΡΡΠ΅ΠΌΡ Π²ΠΏΠΎΠ»Π½Π΅ ΠΏΠΎΠΊΠ°ΡΠΈΡ. Π― ΡΠΎΠ±ΠΈΡΠ°ΡΡΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ΡΡΠΈ ΠΏΠΎΠΊΠ°Π·Π°ΡΠ΅Π»ΠΈ Π΄Π»Ρ ΠΏΠΎΠ½ΠΈΠΆΠ΅Π½ΠΈΡ ΡΠ°ΡΡΠΎΡΡ ΠΊΠΎΠ½ΡΡΠΎΠ»Π»Π΅ΡΠ° ΡΡΠΎΠ±Ρ ΡΠ½ΠΈΠ·ΠΈΡΡ ΠΏΠΎΡΡΠ΅Π±Π»Π΅Π½ΠΈΠ΅.
Π ΡΠ»ΠΎΠ²Ρ, Π² Π½ΠΎΡΠΌΠ°Π»ΡΠ½ΠΎΠΌ ΡΠ΅ΠΆΠΈΠΌΠ΅ Π·Π°Π³ΡΡΠ·ΠΊΠ° ΡΠΎΡΡΠ°Π²ΠΈΠ»Π° ΠΎΠΊΠΎΠ»ΠΎ 12.5% ΠΈ ΠΏΠΎΠ΄ΠΏΡΡΠ³ΠΈΠ²Π°Π΅Ρ Π΄ΠΎ 15.5% ΠΊΠΎΠ³Π΄Π° ΠΏΡΠΈΡ ΠΎΠ΄ΡΡ Π΄Π°Π½Π½ΡΠ΅ ΠΎΡ GPS ΠΈ ΠΈΡ Π½ΡΠΆΠ½ΠΎ ΠΏΠ°ΡΡΠΈΡΡ. ΠΡΠΈ Π²ΡΠΊΠ»ΡΡΠ΅Π½Π½ΠΎΠΌ Π΄ΠΈΡΠΏΠ»Π΅Π΅ (Ρ ΠΎΡΡ GPS ΠΏΡΠΎΠ΄ΠΎΠ»ΠΆΠ°Π΅Ρ ΠΏΠ°ΡΡΠΈΡΡΡ) Π·Π°Π³ΡΡΠ·ΠΊΠ° ΠΏΠ°Π΄Π°Π΅Ρ Π΄ΠΎ 0. ΠΡΠΎ ΡΡΡΠ°Π½Π½ΠΎ. ΠΠΈΠ΄ΠΈΠΌΠΎ ΠΏΠ°ΡΡΠΈΠ½Π³ GPS Π½Π° ΡΠ°ΠΌΠΎΠΌ Π΄Π΅Π»Π΅ Π·Π°Π½ΠΈΠΌΠ°Π΅Ρ ΠΌΠ΅Π½ΡΡΠ΅ ΡΠΈΠΊΠ°, ΠΏΠΎΡΡΠΎΠΌΡ ΠΊΠ°ΠΆΠ΄ΡΠΉ ΡΠΈΠΊ ΠΏΠΎΡΠ»Π΅ ΡΡΠΎΠ³ΠΎ ΡΠ²Π°Π»ΠΈΠ²Π°Π΅ΡΡΡ Π² idle task. ΠΡΠΏΠ»Π΅ΡΠΊ Π·Π°Π³ΡΡΠ·ΠΊΠΈ Π½Π° 3% Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ ΠΎΠ±ΡΡΡΠ½ΡΠ΅ΡΡΡ Π½Π΅ ΡΠ°ΠΌΠΈΠΌ ΠΏΠ°ΡΡΠΈΠ½Π³ΠΎΠΌ Π΄Π°Π½Π½ΡΡ , Π° ΠΏΠ΅ΡΠ΅ΡΡΠ»ΠΊΠΎΠΉ ΠΈΡ Π² Π΄ΡΡΠ³ΠΎΠΉ ΠΏΠΎΡΠΎΠΊ.
Π₯ΠΎΡΡ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ Ρ Π³Π΄Π΅-ΡΠΎ ΡΡΡ ΠΏΠΎΠΏΡΠΎΡΡΡ Π½Π°ΠΊΠΎΡΡΡΠΈΠ».
ΠΠΎΠΊΠ°Π·Π°Π½ΠΈΡ ΡΠ΅ΠΊΡΡΠ΅ΠΉ ΠΈ ΠΌΠ°ΠΊΡΠΈΠΌΠ°Π»ΡΠ½ΠΎΠΉ Π·Π°Π³ΡΡΠ·ΠΊΠΈ ΠΏΡΠΎΡΠ΅ΡΡΠΎΡΠ°. Π‘Π°ΠΌ ΡΠΊΡΠ°Π½ Π±ΡΠ΄Π΅Ρ ΡΠΏΡΡΡΠ°Π½ Π³Π΄Π΅ Π½ΠΈΠ±ΡΠ΄Ρ Π² Π³Π»ΡΠ±ΠΈΠ½Π°Ρ
ΠΌΠ΅Π½Ρ Π½Π°ΡΡΡΠΎΠ΅ΠΊ.
ΠΡΡΠΊΠΎ ΡΠ°Π·Π½ΠΎ
Π ΡΡΠΎΠΉ ΡΠ΅ΠΊΡΠΈΠΈ Ρ ΡΠΎΠ±ΡΠ°Π» ΠΎΡΠ΄Π΅Π»ΡΠ½ΡΠ΅ ΠΏΡΠΎΠ±Π»Π΅ΠΌΠΊΠΈ, ΠΊΠΎΡΠΎΡΡΠ΅ Ρ ΡΠ΅ΡΠ°Π» Π½Π° ΡΠ°Π·Π½ΡΡ ΡΡΠ°Π΄ΠΈΡΡ ΠΏΡΠΎΠ΅ΠΊΡΠ°. ΠΠ΅Π· ΠΊΠ°ΠΊΠΎΠΉ Π»ΠΈΠ±ΠΎ ΠΎΡΠΎΠ±Π΅Π½Π½ΠΎΠΉ ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°ΡΠ΅Π»ΡΠ½ΠΎΡΡΠΈ.
- ΠΠΈΠ±Π»ΠΈΠΎΡΠ΅ΡΠ½Π°Ρ ΡΠ΅Π°Π»ΠΈΠ·Π°ΡΠΈΡ sprintf Π·Π°Π½ΠΈΠΌΠ°Π΅Ρ ΠΎΠ³ΠΎΠ³ΠΎ ΡΠΊΠΎΠ»ΡΠΊΠΎ β 13k. ΠΡΠΈΡΠ»ΠΎΡΡ Π½Π°ΠΏΠΈΡΠ°ΡΡ ΡΠ²ΠΎΡ ΡΠ΅Π°Π»ΠΈΠ·Π°ΡΠΈΡ. Π― Π½Π°ΠΏΠΈΡΠ°Π» Π½Π΅Π±ΠΎΠ»ΡΡΠΎΠΉ ΠΊΠ»Π°ΡΡΠΈΠΊ, ΠΊΠΎΡΠΎΡΡΠΉ ΡΠ΅Π°Π»ΠΈΠ·ΡΠ΅Ρ ΠΈΠ½ΡΠ΅ΡΡΠ΅ΠΉΡ Printable. Π’Π°ΠΊ ΠΌΠΎΠΆΠ½ΠΎ βΠΏΠ΅ΡΠ°ΡΠ°ΡΡβ ΡΠΈΡΠ»Π° Ρ Π½ΡΠΆΠ½ΡΠΌ ΡΠΎΡΠΌΠ°ΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ΠΌ Π½Π° ΡΠΊΡΠ°Π½ ΠΈ Π΄Π°ΠΆΠ΅ Π² Serial. ΠΠΎΠ»ΡΡΠΈΠ»ΠΎΡΡ Π²Π΅ΡΡΠΌΠ° ΡΠΈΠΌΠΏΠ°ΡΠΈΡΠ½Π΅Π½ΡΠΊΠΎ ΠΈ Π²ΡΠ΅Π³ΠΎ ΠΏΠ°ΡΡ ΡΠΊΡΠ°Π½ΠΎΠ² ΠΊΠΎΠ΄Π°.Π€ΠΎΡΠΌΠ°ΡΠΈΡΠΎΠ²ΡΠΈΠΊ ΡΠΈΡΠ΅Π» Ρ ΠΏΠ»Π°Π²Π°ΡΡΠ΅ΠΉ ΡΠΎΡΠΊΠΎΠΉ
/// Helper class to print float numbers according to specified options class FloatPrinter : public Printable { char buf[8]; // Print numbers no longer than 7 digits including sign and point symbols uint8 pos; // position in the buffer with the first meaningful char public: FloatPrinter(float value, uint8 width, bool leadingZeros = false, bool alwaysPrintSign = false); virtual size_t printTo(Print& p) const; }; FloatPrinter::FloatPrinter(float value, uint8 width, bool leadingZeros, bool alwaysPrintSign) { Β Β Β Β // reserve a space for sign Β Β Β Β uint8 minpos = 0; Β Β Β Β if(alwaysPrintSign || value < 0) minpos++; Β Β Β Β // absolute value to print, deal with sign later Β Β Β Β float v = value; Β Β Β Β if(v < 0) v = 0. - v; Β Β Β Β Β Β Β Β // floating point position will depend on the value Β Β Β Β uint8 precision = 0; Β Β Β Β if(v < 100) Β Β Β Β { v *= 10; precision++; Β Β Β Β } Β Β Β Β if(v < 100) // doing this twice { v *= 10; precision++; } uint32 iv = v + 0.5; // we will be operating with integers // Filling the buffer starting from the right pos = width; buf[pos] = ''; bool onceMore = true; // Print at least one zero before dot while((iv > 0 || onceMore) && (pos > minpos)) Β Β Β Β { pos--; onceMore = false; // Fill one digit buf[pos] = iv % 10 + '0'; iv /= 10; // Special case for printing point // Trick used: if precision is 0 here it will become 255 and dot will never be printed (assuming the buffer size is less than 255) if(--precision == 0) { buf[--pos] = '.'; onceMore = true; } Β Β Β Β } Β Β Β Β Β Β Β Β //Print sign Β Β Β Β if(value < 0) buf[--pos] = '-'; Β Β Β Β else if (alwaysPrintSign) buf[--pos] = '+'; } size_t FloatPrinter::printTo(Print& p) const { return p.print(buf+pos); }
- ΠΠΎΠΊΠ° Ρ ΠΏΠΈΡΠ°Π» ΡΡΡ ΡΡΠ½ΠΊΡΠΈΡ Π½ΡΠΆΠ½ΠΎ Π±ΡΠ»ΠΎ Π΅Π΅ ΠΊΠ°ΠΊ ΡΠΎ ΠΏΡΠΎΠ²Π΅ΡΡΡΡ. ΠΠ°ΠΊ-ΡΠΎ ΡΠ°ΠΊ ΠΏΠΎΠ»ΡΡΠΈΠ»ΠΎΡΡ, ΡΡΠΎ Ρ ΠΌΠ΅Π½Ρ Π½Π° Π΄ΠΎΠΌΠ°ΡΠ½Π΅ΠΌ ΠΊΠΎΠΌΠΏΠ΅ Π½Π΅ ΡΡΡΠ°Π½ΠΎΠ²Π»Π΅Π½ΠΎ Π½ΠΈΠΊΠ°ΠΊΠΈΡ
ΠΊΠΎΠΌΠΏΠΈΠ»ΡΡΠΎΡΠΎΠ² ΠΈΠ»ΠΈ IDE ΠΊΡΠΎΠΌΠ΅ Π°ΡΠ΄ΡΠΈΠ½ΠΎ (ΠΈ Atmel Studio). ΠΠΎΡΡΠΎΠΌΡ ΠΊΠΎΠ΄ Ρ ΠΏΠΈΡΠ°Π» Π² Π±ΡΠ°ΡΠ·Π΅ΡΠ΅ Π½Π° cpp.sh. Π― Π½Π°ΠΏΠΈΡΠ°Π» Π½Π΅Π±ΠΎΠ»ΡΡΡΡ ΠΎΠ±Π΅ΡΡΠΊΡ Π²ΠΎΠΊΡΡΠ³ ΡΡΠΎΠ³ΠΎ ΠΊΠΎΠ΄Π°, ΠΊΠΎΡΠΎΡΠ°Ρ Π·Π°ΠΏΡΡΠΊΠ°Π΅Ρ ΡΡΠ½ΠΊΡΠΈΡ Ρ ΡΠ°Π·Π½ΡΠΌΠΈ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠ°ΠΌΠΈ ΠΈ ΠΏΡΠΎΠ²Π΅ΡΡΠ΅Ρ ΡΠ΅Π·ΡΠ»ΡΡΠ°Ρ. ΠΠΎΠ»ΡΡΠΈΠ»ΡΡ ΡΠ°ΠΊΠΎΠΉ ΡΠ΅Π±Π΅ Π²Π°ΡΠΈΠ°Π½Ρ ΡΠ½ΠΈΡ ΡΠ΅ΡΡΠ°, ΡΠΎΠ»ΡΠΊΠΎ Π±Π΅Π· Π½Π΅ΠΎΠ±Ρ
ΠΎΠ΄ΠΈΠΌΠΎΡΡΠΈ ΡΠΎΠ·Π΄Π°Π²Π°ΡΡ ΠΏΡΠΎΠ΅ΠΊΡ, Π²ΡΡΠ³ΠΈΠ²Π°ΡΡ ΡΡΠ΄Π° ΠΊΠ°ΠΊΠΎΠΉ Π½ΠΈΠ±ΡΠ΄Ρ ΡΠ΅ΡΡΠΎΠ²ΡΠΉ ΡΡΠ΅ΠΉΠΌΠ²ΠΎΡΠΊ ΠΈ Π²ΡΠ΅ ΡΠ°ΠΊΠΎΠ΅.ΠΠΎΠ½Π΅ΡΠ½ΠΎ ΠΆΠ΅ Π΅ΡΡΡ ΠΈ ΠΌΠΈΠ½ΡΡΡ. ΠΠΎΠ΄ ΡΠ΅ΡΡΠ° Π½ΡΠΆΠ½ΠΎ ΡΠΈΠ½Ρ
ΡΠΎΠ½ΠΈΠ·ΠΈΡΠΎΠ²Π°ΡΡ Ρ βΠΏΡΠΎΠ΄Π°ΠΊΡΠ΅Π½β ΠΊΠΎΠ΄ΠΎΠΌ ΠΌΠ΅ΡΠΎΠ΄ΠΎΠΌ ΠΊΠΎΠΏΠΈ-ΠΏΠ°ΡΡΡ. ΠΠ»Π°Π³ΠΎ ΡΡΠΎ Π½Π΅ Π½ΡΠΆΠ½ΠΎ Π΄Π΅Π»Π°ΡΡ ΠΎΡΠ΅Π½Ρ ΡΠ°ΡΡΠΎ.ΡΠΈΠΏΠ° ΡΠ½ΠΈΡ ΡΠ΅ΡΡ
#include #include typedef unsigned char uint8; typedef unsigned int uint32; // This is some kind of a unit test for float value print helper. Code under the test is injected into a test function below via simple copy/paste from FloatPrinter constructor. // This allows executing the code right at C++-in-browser service (such as http://cpp.sh) // I just did not want to set up a development toolchain, create a project file, deal with external libraries, do a dependency injection into tested class, etc :) void test(const char * expectedValue, float value, uint8 width, bool leadingZeros = false, bool alwaysPrintSign = false) { Β Β Β Β char buf[9]; Β Β Β Β uint8 pos; Β Β Β Β printf("Printing %f... ", value); //////////////////////////////////////////////////////// // Begin copy from FloatPrinter //////////////////////////////////////////////////////// //////////////////////////////////////////////////////// // End copy from FloatPrinter //////////////////////////////////////////////////////// Β Β Β Β if(strcmp(expectedValue, buf+pos) == 0) Β Β Β Β { Β Β Β Β Β Β Β Β printf("%s - PASSEDn", buf+pos); Β Β Β Β } Β Β Β Β else Β Β Β Β { Β Β Β Β Β Β Β Β printf("%s - FAILEDn", expectedValue); Β Β Β Β Β Β Β Β printf("Got: %sn", buf+pos); Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β Β printf("Buffer: "); for(int i=0; i<9; i++) Β Β Β Β printf("%2x ", buf[i]); Β Β Β Β printf("npos=%dnn", pos); Β Β Β Β } } int main() { Β Β Β Β test("0", 0., 4); Β Β Β Β test("0.10", 0.1, 4); Β Β Β Β test("0.23", 0.23, 4); Β Β Β Β test("4.00", 4., 4); Β Β Β Β test("5.60", 5.6, 4); Β Β Β Β test("7.89", 7.89, 4); Β Β Β Β test("1.23", 1.234, 4); Β Β Β Β test("56.8", 56.78, 4); Β Β Β Β test("56.8", 56.78, 5); Β Β Β Β test("123", 123.4, 4); Β Β Β Β test("568", 567.8, 5); Β Β Β Β test("12345", 12345., 6); Β Β Β Β test("-0.10", -0.1, 5); Β Β Β Β test("-0.23", -0.23, 5); Β Β Β Β test("-4.00", -4., 5); Β Β Β Β test("-5.60", -5.6, 5); Β Β Β Β test("-7.89", -7.89, 5); Β Β Β Β test("-1.23", -1.234, 5); Β Β Β Β test("-56.8", -56.78, 5); Β Β Β Β test("-56.8", -56.78, 6); Β Β Β Β test("-123", -123.4, 5); Β Β Β Β test("-568", -567.8, 6); Β Β Β Β test("-12345", -12345., 7); }
- ΠΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ Serial.print Π² ΠΊΠΎΠ½ΡΡΡΡΠΊΡΠΎΡΠ°Ρ Π½Π΅Π»ΡΠ·Ρ β ΠΠ ΡΡ ΠΎΠ΄ΠΈΡ Π² ΡΠΈΠΊΠ»ΠΈΡΠ΅ΡΠΊΠΈΠΉ ΡΠ΅Π±ΡΡ. Π‘ΠΊΠΎΡΠ΅Π΅ Π²ΡΠ΅Π³ΠΎ USB Serial ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·ΠΈΡΡΠ΅ΡΡΡ Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΎ ΠΏΠΎΠ·ΠΆΠ΅ ΠΊΠΎΠ½ΡΡΡΡΠΊΡΠΎΡΠΎΠ² ΡΡΠ°ΡΠΈΡΠ΅ΡΠΊΠΈ ΡΠ°Π·ΠΌΠ΅ΡΠ΅Π½Π½ΡΡ ΠΎΠ±ΡΠ΅ΠΊΡΠΎΠ². ΠΠ·-Π·Π° ΡΡΠΎΠ³ΠΎ Π²ΡΠ·ΡΠ²Π°Π΅ΡΡΡ Π½Π΅ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·ΠΈΡΠΎΠ²Π°Π½Π½ΡΠΉ ΠΊΠΎΠ΄.
- Π‘ΡΠ°Π½Π΄Π°ΡΡΠ½ΡΠΉ ΡΠΈΠ½Π³Π»ΡΠΎΠ½ΡΠΈΠΊ ΠΠ°ΠΉΠ΅ΡΡΠ° ΠΏΡΠΈΠ½Π΅Ρ Π² ΠΏΡΠΎΠ΅ΠΊΡ ΡΠ°ΠΊΠΎΠ΅ ΠΎΠ³ΡΠΎΠΌΠ½ΠΎΠ΅ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎ ΠΊΠΎΠ΄Π°, ΡΡΠΎ ΠΌΠ°ΠΌΠ° Π½Π΅ Π³ΠΎΡΡΠΉ. ΠΠΎΠ»Π΅Π΅ 40ΠΊ! Π’Π°ΠΌ Π±ΡΠ»ΠΈ ΠΈ ΡΠΊΡΠ΅ΠΏΡΠ΅Π½Ρ, ΠΈ type info, ΠΊΠ°ΠΊΠΈΠ΅ ΡΠΎ ΠΊΡΡΠΊΠΈ C++ ABI ΠΈ ΠΌΠ½ΠΎΠ³ΠΎ ΡΠ΅Π³ΠΎ Ρ ΠΈ ΡΠ»ΡΡ
ΠΎΠΌ Π½Π΅ ΡΠ»ΡΡ
ΠΈΠ²Π°Π» Π·Π° Π΄Π΅ΡΡΡΠΈΠ»Π΅ΡΠΈΡ ΡΠ°Π±ΠΎΡΡ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΠΈΡΡΠΎΠΌ.Π°Π³Π°, Π²ΠΎΡ ΡΡΠΈ ΡΠ΅Π±ΡΡΠ°
GPSDataModel & GPSDataModel::instance() { Β Β Β static GPSDataModel inst; Β Β Β return inst; }
Π’Π°ΠΊ ΡΡΠΎ ΠΏΠ»ΡΡΡ ΠΏΠ»ΡΡΠ°ΠΌΠΈ, Π° ΠΎΠ±ΡΠ΅ΠΊΡΡ ΠΏΡΠΈΡΠ»ΠΎΡΡ ΡΠ°Π·ΠΌΠ΅ΡΡΠΈΡΡ Π² Π³Π»ΠΎΠ±Π°Π»ΡΠ½ΡΡ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ , ΠΏΡΠΎΠΏΠΈΡΠ°Π² Π³Π΄Π΅ Π½ΡΠΆΠ½ΠΎ ΡΡΡΠ»ΠΊΠΈ ΡΠ΅ΡΠ΅Π· extern.
- Π¨ΡΠΈΡΡΡ. Π ΠΏΡΠΎΡΠ»ΠΎΠΉ ΡΠ°ΡΡΠΈ Ρ ΡΠΆΠ΅ ΠΏΠΈΡΠ°Π», ΡΡΠΎ ΡΡΠΈΡΡΡ ΠΏΡΠΈΡΠ»ΠΎΡΡ Π³ΠΎΡΠΎΠ²ΠΈΡΡ ΡΠ°ΠΌΠΎΡΡΠΎΡΡΠ΅Π»ΡΠ½ΠΎ. Π£ ΠΌΠ΅Π½Ρ Π΅ΡΡΡ ΡΠΊΡΠΈΠΏΡ, ΠΊΠΎΡΠΎΡΡΠΉ ΠΈΠ· ΠΊΠ°ΡΡΠΈΠ½ΠΊΠΈ Π³Π΅Π½Π΅ΡΠΈΡ Π½ΡΠΆΠ½ΡΠΉ ΠΊΠΎΠ΄. ΠΠΎ Π² ΠΈΠ·Π½Π°ΡΠ°Π»ΡΠ½ΠΎΠΌ Π²Π°ΡΠΈΠ°Π½ΡΠ΅ Π΄Π°Π½Π½ΡΠ΅ Π»Π΅ΠΆΠ°Π»ΠΈ Π½Π΅ Π² ΡΠΏΠ°ΠΊΠΎΠ²Π°Π½Π½ΠΎΠΌ ΡΠΎΡΠΌΠ°ΡΠ΅, Π° ΠΏΠΎΡΠΎΠΌΡ Π·Π°Π½ΠΈΠΌΠ°Π»ΠΈ Π±ΠΎΠ»ΡΡΠ΅ ΠΌΠ΅ΡΡΠ° ΡΠ΅ΠΌ ΠΌΠΎΠ³Π»ΠΈ Π±Ρ. Π― Π΄ΠΎΡΠ°Π±ΠΎΡΠ°Π» ΡΠΊΡΠΈΠΏΡ, ΠΈ ΡΡΠΈΡΡΡ ΡΠ΄Π°Π»ΠΎΡΡ ΡΠΏΠ°ΠΊΠΎΠ²Π°ΡΡ, ΡΠ΅ΠΌ ΡΠ°ΠΌΡΠΌ Π²ΡΠΈΠ³ΡΠ°Π² ΡΡΡΠΎΠΊ ΡΠ»Π΅ΡΠ°.Π’Π°ΠΊ, ΡΡΠΈΡΡ 8Ρ 12 ΠΏΠΎΡ ΡΠ΄Π΅Π» Ρ 850 Π΄ΠΎ 732 Π±Π°ΠΉΡΠ°, Π° ΡΡΠΈΡΡ 16Ρ 22 (Π½Π°ΡΠΈΡΠΎΠ²Π°Π» ΡΠ°ΠΌΠΎΡΡΠΎΡΡΠ΅Π»ΡΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΡ Bodoni MT) ΡΠΌΠ΅Π½ΡΡΠΈΠ»ΡΡ Ρ 474 Π΄ΠΎ 408. Π ΡΡΠΎΠΌ ΡΡΠΈΡΡΠ΅ ΡΠΎΠ»ΡΠΊΠΎ ΡΠΈΡΡΡ, ΠΏΠΎΡΠΎΠΌΡ ΠΎΠ½ ΡΠ°ΠΊ ΠΌΠ°Π»ΠΎ Π·Π°Π½ΠΈΠΌΠ°Π΅Ρ.
- ΠΠ·Π½Π°ΡΠ°Π»ΡΠ½ΠΎ ΡΡΠΈΡΡΡ Ρ ΠΌΠ΅Π½Ρ ΡΠ°ΡΠΏΠΎΠ»Π°Π³Π°Π»ΠΈΡΡ Π² Ρ Π΅Π΄Π΅ΡΠ΅ Π² Π²ΠΈΠ΄Π΅ ΠΊΠΎΠ½ΡΡΠ°Π½ΡΠ½ΡΡ ΠΌΠ°ΡΡΠΈΠ²ΠΎΠ². Π₯Π΅Π΄Π΅ΡΡ ΠΈΠ½ΠΊΠ»ΡΠ΄ΡΡΡΡ Π² ΡΠΎΠΎΡΠ²Π΅ΡΡΡΠ²ΡΡΡΠΈΠ΅ cpp-ΡΠ½ΠΈΠΊΠΈ Π³Π΄Π΅ Ρ ΡΠΈΡΡΡ ΡΡΠΈΠΌΠΈ ΡΡΠΈΡΡΠ°ΠΌΠΈ. Π’Π°ΠΊ Π²ΠΎΡ, ΡΡΡΠΊΠΎ, ΠΊΠΎΠΌΠΏΠΈΠ»Π΅Ρ Π΄Π»Ρ ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΡΠΏΠΏΡΠ½ΠΈΠΊΠ° Π΄ΡΠ±Π»ΠΈΡΡΠ΅Ρ ΡΠΎΠ½ΡΡ. Π― ΠΏΠ΅ΡΠ΅ΠΌΠ΅ΡΡΠΈΠ» ΡΠΎΠ½ΡΡ Π² cpp ΡΠ°ΠΉΠ» ΠΈ ΠΎΠ±ΡΠ΅ΠΌ ΡΠ»Π΅ΡΠ° ΡΡΠ°Π·Ρ ΡΠΎΠΊΡΠ°ΡΠΈΠ»ΡΡ Π½Π° 9ΠΊ. 9 ΠΊΠΈΠ»ΠΎΠ±Π°ΠΉΡ Π·Π° ΠΊΠΎΠ½ΡΡΠ°Π½ΡΡ Π² Ρ Π΅Π΄Π΅ΡΠ΅! 9 ΠΊΠΈΠ»ΠΎΠ±Π°ΠΉΡ, ΠΠ°ΡΠ»! ΠΠΎΡ ΠΈ Π²Π΅ΡΡ ΠΏΠΎΡΠ»Π΅ ΡΡΠΎΠ³ΠΎ ΠΎΡΠ»Π°ΠΆΠ΅Π½Π½ΡΠΌ ΠΊΠΎΠΌΠΏΠΈΠ»ΡΡΠΎΡΠ°ΠΌ!
- Π‘ ΡΠ΄ΠΈΠ²Π»Π΅Π½ΠΈΠ΅ΠΌ ΠΎΠ±Π½Π°ΡΡΠΆΠΈΠ», ΡΡΠΎ Ρ HardwareSerial Π½Π΅Ρ ΠΌΠ΅ΡΠΎΠ΄Π° attachInterrupt. Π£ ΠΎΡΠΈΠ³ΠΈΠ½Π°Π»ΡΠ½ΠΎΠ³ΠΎ Π°ΡΠ΄ΡΠΈΠ½ΠΎ Π΅Π³ΠΎ, ΠΊΡΡΠ°ΡΠΈ, ΡΠΎΠΆΠ΅ Π½Π΅Ρ. ΠΠΎΠΆΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡΡΡ NeoSWSerial, ΠΊΠΎΡΠΎΡΡΠΉ ΡΠ΅ΠΊΠΎΠΌΠ΅Π½Π΄ΡΡΡ Π² Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΠΈ NeoGPS, Π½ΠΎ ΡΡΠΎ ΠΊΠ°ΠΊ ΡΠΎ ΡΡΡΠ°Π½Π½ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ΡΠΎΡΡΠ²Π°ΡΠ½ΡΠΉ UART ΠΏΡΠΈ Π½Π°Π»ΠΈΡΠΈΠΈ ΡΠ΅Π»ΠΎΠΉ ΠΊΡΡΠΈ Ρ Π°ΡΠ΄Π²Π°ΡΠ½ΡΡ .Π― ΡΡΡ ΠΏΡΠΎΡΡΠΎ Π½Π°ΡΠΈΡΠ°Π»ΡΡ Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΠΈ ΠΏΠΎ STM32 β DMA ΠΈ Π²ΡΠ΅ ΡΠ°ΠΊΠΎΠ΅. ΠΠΎΠ΄ΡΠΌΠ°Π», ΠΌΠΎΠΆΠ΅Ρ ΠΈ ΠΌΠ½Π΅ ΠΏΡΠΈΠ³ΠΎΠ΄ΠΈΡΡΡ ΡΠ°ΠΊΠΎΠΉ ΡΠ΅ΠΆΠΈΠΌ Π² ΡΠ΅Π»ΡΡ ΡΠΊΠΎΠ½ΠΎΠΌΠΈΠΈ Π±Π°ΡΠ°ΡΠ΅ΠΈ. ΠΠ΅Π΄Ρ ΡΠ΅ΠΉΡΠ°Ρ Ρ ΠΎΡΡ ΠΈ ΡΠΎ sleep()βΠ°ΠΌΠΈ, Π½ΠΎ Π²ΡΠ΅ ΡΠ°ΠΊΠΈ ΠΈΠ΄Π΅Ρ ΠΏΠΎΡΡΠΎΡΠ½Π½ΡΠΉ ΠΎΠΏΡΠΎΡ βΠ° Π½Π΅ ΠΏΡΠΈΡΠ»ΠΎ Π»ΠΈ ΡΠ΅Π³ΠΎ ΠΈΠ· GPS?β
- ΠΡΠΈΡΠ»ΠΎΡΡ ΠΎΠ·Π°Π΄Π°ΡΠΈΡΡΡ Π²ΠΎΠΏΡΠΎΡΠ°ΠΌΠΈ UX Π΄ΠΈΠ·Π°ΠΉΠ½Π°. Π₯ΠΎΡΠ΅ΡΡΡ Π²ΡΠ²Π΅ΡΡΠΈ Π½Π° ΡΠΊΡΠ°Π½ ΠΊΡΡΡ ΡΠ°Π·Π½ΠΎΠΉ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΠΈ, Π½ΠΎ ΠΏΠΈΠΊΡΠ΅Π»Π΅ΠΉ Π½Π΅ ΡΠ°ΠΊ ΠΌΠ½ΠΎΠ³ΠΎ. ΠΠ°ΠΆΠ΅ Ρ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ΠΌ ΡΠ°ΠΌΠΎΠ³ΠΎ ΠΌΠ°Π»Π΅Π½ΡΠΊΠΎΠ³ΠΎ ΡΡΠΈΡΡΠ° Π²Π»Π°Π·ΠΈΡ Π½Π΅ Π±ΠΎΠ»ΡΡΠ΅ 3 ΡΡΡΠΎΠΊ ΠΏΠΎ 21 ΡΠΈΠΌΠ²ΠΎΠ»Ρ.Π’Π°ΠΊ, Π²ΡΡ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ ΠΏΠΎ ΠΎΠ΄ΠΎΠΌΠ΅ΡΡΡ ΠΏΠΎΠΏΡΠΎΡΡΡ Π½Π΅ Π²Π»Π°Π·ΠΈΡ Π½Π° ΠΎΠ΄ΠΈΠ½ ΡΠΊΡΠ°Π½. Π Π½Π° 2 ΡΠΎΠΆΠ΅. ΠΡΠΈΡΠ»ΠΎΡΡ ΡΠ΄Π΅Π»Π°ΡΡ ΠΎΠ΄ΠΈΠ½ ΠΎΡΠ½ΠΎΠ²Π½ΠΎΠΉ ΡΠΊΡΠ°Π½ Ρ Π±ΠΎΠ»ΡΡΠΈΠΌΠΈ ΠΈ ΠΊΡΠ°ΡΠΈΠ²ΡΠΌΠΈ Π±ΡΠΊΠ²Π°ΠΌΠΈ. Π Π΅ΡΠ»ΠΈ ΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΠ΅Π»Ρ ΠΈΠ½ΡΠ΅ΡΠ΅ΡΠ½ΠΎ, ΡΠΎ Π² ΡΠ΅ΠΆΠΈΠΌΠ΅ ΠΏΠΎΠ΄ΠΌΠ΅Π½Ρ ΠΌΠΎΠΆΠ½ΠΎ Π±ΡΠ΄Π΅Ρ Π΄ΠΎΡΡΡΠΏΠΈΡΡΡ Π΄ΠΎ Π±ΠΎΠ»Π΅Π΅ Π΄Π΅ΡΠ°Π»ΡΠ½ΠΎΠΉ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΠΈ Π½Π° Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΈΡ ΡΠΊΡΠ°Π½ΡΠΈΠΊΠ°Ρ .
- GPS. ΠΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° NeoGPS ΠΏΡΠ΅Π΄ΠΎΡΡΠ°Π²Π»ΡΠ΅Ρ Π½Π΅ΠΊΠΈΠΉ ΡΡΠ°ΡΡΡ ΡΠΎΠ΅Π΄ΠΈΠ½Π΅Π½ΠΈΡ. Π ΡΡΠΈΠ»Π΅ βΠ½Π΅Ρ ΡΠΈΠ³Π½Π°Π»Π°β -> βΠΏΠΎΠ»ΡΡΠΈΠ»ΠΈ ΡΠΎΠ»ΡΠΊΠΎ Π²ΡΠ΅ΠΌΡβ -> βΠΏΠΎΠ»ΡΡΠΈΠ»ΠΈ 2D Fixβ -> βΠΏΠΎΠ»ΡΡΠΈΠ»ΠΈ 3D Fixβ. ΠΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ ΡΡΠΎ ΡΠ°Π±ΠΎΡΠ°Π΅Ρ Ρ ΠΊΠ°ΠΊΠΈΠΌ Π½ΠΈΠ±ΡΠ΄Ρ Π΄ΡΡΠ³ΠΈΠΌ GPS ΠΌΠΎΠ΄ΡΠ»Π΅ΠΌ, Π½ΠΎ Π½Π΅ Ρ ΠΌΠΎΠΈΠΌ. Π£ ΠΌΠ΅Π½Ρ ΡΠ°Π±ΠΎΡΠ°ΡΡ ΡΠΎΠ»ΡΠΊΠΎ ΠΏΠ΅ΡΠ²ΡΠΉ ΠΈ ΠΏΠΎΡΠ»Π΅Π΄Π½ΠΈΠΉ ΠΏΡΠ½ΠΊΡΡ.
- GPS. Π₯ΠΎΡΠ΅Π» ΠΏΠΎΠ±ΡΡΠΈΠΊΡ ΡΠ°Π·Π΄ΠΎΠ±ΡΡΡ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡ ΡΠΎΡΠ½ΠΎΡΡΠΈ ΠΊΠΎΠΎΡΠ΄ΠΈΠ½Π°ΡΡ. Π Π½Π΅ ΡΡΡ-ΡΠΎ Π±ΡΠ»ΠΎ. Π ΡΠ²Π½ΠΎΠΌ Π²ΠΈΠ΄Π΅ ΠΏΠΎΠ»ΡΡΠΈΡΡ Π΅Π³ΠΎ Π½Π΅ ΡΠ΄Π°Π»ΠΎΡΡ, Π° HDOP/VDOP ΡΡΠΎ ΡΠΎΠ»ΡΠΊΠΎ ΠΊΠΎΡΠ²Π΅Π½Π½ΡΠ΅ ΠΏΠΎΠΊΠ°Π·Π°ΡΠ΅Π»ΠΈ ΡΠΎΡΠ½ΠΎΡΡΠΈ. ΠΡΠ΄Ρ ΠΎΡΠ΅Π½Ρ Π±Π»Π°Π³ΠΎΠ΄Π°ΡΠ΅Π½ Π·Π° ΡΠΎΠ»ΠΊΠΎΠ²ΠΎΠ΅ ΡΠ°Π·ΡΡΡΠ½Π΅Π½ΠΈΠ΅ ΠΏΠΎ ΡΡΠΈΠΌ ΠΌΠ΅ΡΡΠΈΠΊΠ°ΠΌ.
- ΠΡΡΠΎΡΠ° ΠΌΠΎΠΆΠ΅Ρ Π±ΡΡΡ ΠΎΡΡΠΈΡΠ°ΡΠ΅Π»ΡΠ½Π°Ρ. ΠΡΠ΅Π½Ρ ΡΠ΄ΠΈΠ²ΠΈΠ»ΡΡ ΠΊΠΎΠ³Π΄Π° ΡΠ²ΠΈΠ΄Π΅Π» Π²ΡΡΠΎΡΡ 65000ΠΌ, ΠΎΠΊΠ°Π·Π°Π»ΠΎΡΡ ΠΠΠ‘ ΠΏΠΎΡΠ»Π΅ Π²ΠΊΠ»ΡΡΠ΅Π½ΠΈΡ Π΄Π°Π²Π°Π» Π²ΡΡΠΎΡΡ -500ΠΌ. ΠΡΠΈΡΠ»ΠΎΡΡ ΡΠ΄Π΅Π»Π°ΡΡ ΡΠΏΠ΅ΡΠΈΠ°Π»ΡΠ½ΡΠΉ ΠΊΠ΅ΠΉΡ Ρ ΡΠ΅Π±Ρ Π² ΠΊΠΎΠ΄Π΅ Π΄Π»Ρ ΠΊΠΎΡΡΠ΅ΠΊΡΠ½ΠΎΠ³ΠΎ ΠΎΡΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡ ΠΎΡΡΠΈΡΠ°ΡΠ΅Π»ΡΠ½ΡΡ Π²ΡΡΠΎΡ.
- Π‘ΠΊΠΎΡΠΎΡΡΡ ΠΏΠΎΠ·ΠΈΡΠΈΠΎΠ½ΠΈΡΠΎΠ²Π°Π½ΠΈΡ ΠΎΡΡΠ°Π²Π»ΡΠ΅Ρ ΠΆΠ΅Π»Π°ΡΡ Π»ΡΡΡΠ΅Π³ΠΎ. Π Π΅ΠΊΠ»Π°ΠΌΠ° Π³Π»Π°ΡΠΈΡ Time To First Fix < 30 ΡΠ΅ΠΊΡΠ½Π΄, Π½ΠΎ ΡΡΠΎ, ΠΏΠΎ Π²ΡΠ΅ΠΉ Π²ΠΈΠ΄ΠΈΠΌΠΎΡΡΠΈ, ΠΎΠ·Π½Π°ΡΠ°Π΅Ρ ΠΏΠΎΠΈΠΌΠΊΡ ΠΏΠ΅ΡΠ²ΠΎΠ³ΠΎ ΡΠΏΡΡΠ½ΠΈΠΊΠ°, Π° Π½Π΅ ΠΏΠ΅ΡΠ²ΡΡ ΠΊΠΎΠΎΡΠ΄ΠΈΠ½Π°Ρ. ΠΡΠ΅ΠΌΡ ΡΠ΅Π³ΠΈΡΡΡΠΈΡΡΠ΅ΡΡΡ ΠΏΠΎΡΡΠΈ ΡΡΠ°Π·Ρ ΠΏΠΎΡΠ»Π΅ Π²ΠΊΠ»ΡΡΠ΅Π½ΠΈΡ. Π Π²ΠΎΡ ΠΊΠΎΠΎΡΠ΄ΠΈΠ½Π°Ρ ΠΏΡΠΈΡ ΠΎΠ΄ΠΈΡΡΡ ΠΆΠ΄Π°ΡΡ ΠΏΠ°ΡΡ ΠΌΠΈΠ½ΡΡ. ΠΠ°ΠΆΠ΅ GPS Π²ΠΊΠ»ΡΡΠ°Π»ΡΡ Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΎ ΠΌΠΈΠ½ΡΡ Π½Π°Π·Π°Π΄.ΠΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ ΠΌΠΎΠ΄ΡΠ»Ρ Π½Π΅ Π½ΡΠΆΠ½ΠΎ Π²ΡΠΊΠ»ΡΡΠ°ΡΡ Β«ΠΈΠ· ΡΠΎΠ·Π΅ΡΠΊΠΈΒ» Π° ΠΏΠ΅ΡΠ΅Π²ΠΎΠ΄ΠΈΡΡ Π² ΠΊΠ°ΠΊΠΎΠΉ Π½ΠΈΠ±ΡΠ΄Ρ Π³Π»ΡΠ±ΠΎΠΊΠΈΠΉ ΡΠΎΠ½. ΠΠ°ΡΠ°ΡΠ΅ΠΉΠΊΠ° Π½Π° ΠΌΠΎΠ΄ΡΠ»Π΅ Π½Π°ΠΌΠ΅ΠΊΠ°Π΅Ρ.
- Π’ΠΎΡΠ½ΠΎΡΡΡ ΡΠ°ΠΊΠΆΠ΅ ΠΏΠΎΠ΄ Π²ΠΎΠΏΡΠΎΡΠΎΠΌ. ΠΡΡΠΎΡΠ° ΡΠ΅Π³ΠΈΡΡΡΠΈΡΡΠ΅ΡΡΡ Π½Π΅ΠΏΡΠ°Π²ΠΈΠ»ΡΠ½ΠΎ: +-50ΠΌ, ΠΈ ΡΠΎΠ»ΡΠΊΠΎ ΠΏΠΎΡΠΎΠΌ ΠΌΠ΅Π΄Π»Π΅Π½Π½ΠΎ ΠΏΠΎΠ»Π·Π΅Ρ ΠΊΡΠ΄Π° Π½Π°Π΄ΠΎ. ΠΠ° ΠΈ ΠΏΠΎΡΠΎΠΌ Π·Π°ΠΌΠ΅ΡΠ½ΠΎ ΠΏΠ»Π°Π²Π°Π΅Ρ
- ΠΡΠΈ ΠΏΠ»ΠΎΡ ΠΎΠΌ ΠΏΡΠΈΠ΅ΠΌΠ΅ ΠΌΠΎΠ³ΡΡ Π²ΠΎΠ·Π½ΠΈΠΊΠ°ΡΡ Π±ΠΎΠ»ΡΡΠΈΠ΅ ΡΠΊΠ°ΡΠΊΠΈ ΡΠΊΠΎΡΠΎΡΡΠΈ Π΄ΠΎ 150ΠΊΠΌ/Ρ, Π° ΡΠ°ΠΌ ΠΌΠΎΠ΄ΡΠ»Ρ ΠΌΠΎΠΆΠ΅Ρ Π½Π°ΠΊΡΡΡΠΈΡΡ Π½Π° Π΄ΠΎ 7ΠΊΠΌ Π·Π° ΡΠ°Ρ. ΠΡΠΆΠ½ΠΎ Π±ΡΠ΄Π΅Ρ ΠΏΡΠΎΠ²Π΅ΡΠΈΡΡ Π³Π΄Π΅ Π½ΠΈΠ±ΡΠ΄Ρ Π² ΠΏΠΎΠ»Π΅.
ΠΠΏΡΠΈΠΌΠΈΠ·ΠΈΡΡΠ΅ΠΌ
ΠΠ°ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠΊ ΠΏΠ°ΡΡ ΡΠ»ΠΎΠ² ΠΏΡΠΎ ΠΎΠΏΡΠΈΠΌΠΈΠ·Π°ΡΠΈΡ ΠΏΠΎΡΡΠ΅Π±Π»Π΅Π½ΠΈΡ. ΠΠ°, ΠΊΠΎΠ½ΡΡΠΎΠ»Π»Π΅Ρ ΠΌΠΎΡΠ½Π΅Π΅, Π½ΠΎ ΠΏΡΠΎΠ±Π»Π΅ΠΌΡ Π²ΡΠ΅ ΡΠ΅ ΠΆΠ΅. ΠΡΠΆΠ½ΠΎ ΠΎΡΠ΅Π½Ρ Π²Π½ΠΈΠΌΠ°ΡΠ΅Π»ΡΠ½ΠΎ ΡΠ»Π΅Π΄ΠΈΡΡ Π·Π° ΠΏΠΎΡΡΠ΅Π±Π»Π΅Π½ΠΈΠ΅ΠΌ ΠΏΠ°ΠΌΡΡΠΈ ΠΈΠ±ΠΎ ΠΎΠ΄Π½ΠΎ Π½Π΅ΠΎΡΡΠΎΡΠΎΠΆΠ½ΠΎΠ΅ Π΄Π²ΠΈΠΆΠ΅Π½ΠΈΠ΅ ΠΌΠΎΠΆΠ΅Ρ Π΄ΠΎΠ±Π°Π²ΠΈΡΡ ΠΊ ΠΏΡΠΎΡΠΈΠ²ΠΊΠ΅ ΠΏΠ°ΡΡ ΠΊΠΈΠ»ΠΎ.
ΠΠ°ΠΊ ΠΈ ΠΎΠΆΠΈΠ΄Π°Π»ΠΎΡΡ, Π½Π° STM32 Π²ΡΠ»Π΅Π·Π»ΠΈ Π²ΡΠ΅ ΡΠ΅ ΠΆΠ΅ ΠΏΡΠΎΠ±Π»Π΅ΠΌΡ ΡΡΠΎ ΠΈ Π½Π° AVR.
- ΠΊΠΎΠ½ΡΡΠ°Π½ΡΡ, ΠΊΠΎΡΠΎΡΡΠΌ Π·Π°Π±ΡΠ»ΠΈ Π½Π°ΠΏΠΈΡΠ°ΡΡ ΡΠ»ΠΎΠ²ΠΎ const ΠΏΠΎ ΠΏΡΠ΅ΠΆΠ½Π΅ΠΌΡ ΡΠ°Π·ΠΌΠ΅ΡΠ°ΡΡΡΡ Π² ΠΠΠ£ (ΡΠ°ΠΌ Π½Π° ΠΏΠΎΠ»ΠΊΠΈΠ»ΠΎ Π±ΡΠ΄Π΅Ρ ΡΠ°ΠΊΠΈΡ ΠΊΠΎΠ½ΡΡΠ°Π½Ρ. Π ΠΎΡΠ½ΠΎΠ²Π½ΠΎΠΌ USB Π΄Π΅ΡΠΊΡΠΈΠΏΡΠΎΡΡ)
- 512 Π±Π°ΠΉΡ ΠΊΠ°ΡΡΠΈΠ½ΠΊΠΈ adafruit, ΠΊΠΎΡΠΎΡΠ°Ρ Π·Π°Π³ΡΡΠΆΠ°Π΅ΡΡΡ Π² Π±ΡΡΠ΅Ρ Π΄ΠΈΡΠΏΠ»Π΅Ρ ΠΈ Π½ΠΈΠΊΠΎΠ³Π΄Π° Π½Π΅ ΠΏΠΎΠΊΠ°Π·ΡΠ²Π°Π΅ΡΡΡ.
- ΡΡΠ½ΠΊΡΠΈΠΈ ΠΏΠΎ ΡΠ°Π±ΠΎΡΠ΅ Ρ SPI, Ρ ΠΎΡΡ Π½ΠΈΡΠ΅Π³ΠΎ Ρ ΠΌΠ΅Π½Ρ ΠΏΠΎ SPI Π½Π΅ ΠΏΠΎΠ΄ΠΊΠ»ΡΡΠ΅Π½ΠΎ β 512 Π±Π°ΠΉΡ
- Π²ΡΡΠΊΠ°Ρ ΡΠΈΠ³Π½Ρ ΠΈΠ· NeoGPS β Π²ΡΡΠΈΡΠ»Π΅Π½ΠΈΠ΅ Π²ΠΈΡΠΎΠΊΠΎΡΠ½ΠΎΠ³ΠΎ Π³ΠΎΠ΄Π° ΠΈ Π²ΡΠ΅ ΡΠ°ΠΊΠΎΠ΅ ΠΏΡΠΎΡΠ΅Π΅. ΠΠ΅ΠΌ-ΡΠΎ ΠΊΠΎΡΠ²Π΅Π½Π½ΠΎ ΡΠ·Π°Π΅ΡΡΡ β 300 Π±Π°ΠΉΡ
- ΠΊΠ»Π°ΡΡ TwoWire (ΡΡΡΠ½Π°Ρ ΡΠ΅Π°Π»ΠΈΠ·Π°ΡΠΈΡ I2C). ΠΡΠΎ ΡΠΎΡΠ½ΠΎ Π½Π΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ, Π½ΠΎ Π»ΠΈΠ½ΠΊΠ΅Ρ Π΅Π΅ Π²ΡΠ΅ ΡΠ°Π²Π½ΠΎ Π²ΡΡΡ ΠΈΠ²Π°Π΅Ρ β 650 Π±Π°ΠΉΡ
- ΠΊΠΎΠ΄ ΠΏΠΎ ΡΠ°Π±ΠΎΡΠ΅ Ρ ΠΠ¦Π. ΠΠΎΠΊΠ° Π½Π΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ, Π½ΠΎ Π±ΡΠ΄Π΅Ρ ΠΊΠΎΠ³Π΄Π°-ΡΠΎ Π΄Π»Ρ ΠΈΠ·ΠΌΠ΅ΡΠ΅Π½ΠΈΡ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠΎΠ² Π±Π°ΡΠ°ΡΠ΅ΠΈ. ΠΠΎΠΊΠ° Π½Π΅ ΡΡΠΎΠ³Π°Π».
Π‘ΠΏΠΈΡΠΎΠΊ Π΄Π°Π»Π΅ΠΊΠΎ Π½Π΅ ΠΏΠΎΠ»ΠΎΠ½. Π’Π°ΠΊΠΎΠ΅ Π²ΠΏΠ΅ΡΠ°ΡΠ»Π΅Π½ΠΈΠ΅, ΡΡΠΎ Π΅ΡΠ»ΠΈ ΠΊΠ°ΠΊΠΎΠΉ-ΡΠΎ ΠΎΠ±ΡΠ΅ΠΊΡ (ΡΠΎΡ ΠΆΠ΅ TwoWire) ΠΎΠ±ΡΡΠ²Π»Π΅Π½ Π² Ρ Π΅Π΄Π΅ΡΠ΅, ΡΠΎ Π»ΠΈΠ½ΠΊΠ΅Ρ Π΅Π³ΠΎ ΠΏΡΠΈΡΡΠ³ΠΈΠ²Π°Π΅Ρ Π² ΠΏΡΠΎΠ΅ΠΊΡ Π½Π΅Π·Π°Π²ΠΈΡΠΈΠΌΠΎ ΠΎΡ ΡΠΎΠ³ΠΎ, ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ ΠΎΠ½ ΡΠ΅Π°Π»ΡΠ½ΠΎ ΠΈΠ»ΠΈ Π½Π΅Ρ. ΠΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ, ΡΡΠΎ ΠΌΠΎΠΆΠ½ΠΎ ΡΠ΅Π³ΡΠ»ΠΈΡΠΎΠ²Π°ΡΡ Π½Π°ΡΡΡΠΎΠΉΠΊΠ°ΠΌΠΈ Π»ΠΈΠ½ΠΊΠ΅ΡΠ°, Π½ΠΎ Π±ΠΈΠ»Π΄ ΡΠΈΡΡΠ΅ΠΌΠ° Π°ΡΠ΄ΡΠΈΠ½ΠΎ Π½Π΅ ΠΏΠΎΠ·Π²ΠΎΠ»ΡΠ΅Ρ Π½ΠΈΡΠ΅Π³ΠΎ Π½Π°ΡΡΡΠ°ΠΈΠ²Π°ΡΡ. Π ΠΊΠΎΠ½ΡΠ΅ ΠΊΠΎΠ½ΡΠΎΠ² Ρ ΠΏΡΠΎΡΡΠΎ Π·Π°ΠΊΠΎΠΌΠΌΠ΅Π½ΡΠΈΡΠΎΠ²Π°Π» ΠΊΠ»Π°ΡΡ TwoWire Π² Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ΅ Wire ΠΈ Π²ΡΠ΅ ΡΠΊΠΎΠΌΠΏΠΈΠ»ΠΈΠ»ΠΎΡΡ Π±Π΅Π· ΠΏΡΠΎΠ±Π»Π΅ΠΌ.
Π‘ ΠΊΠΎΠ΄ΠΎΠΌ SPI ΡΡΡΡ ΡΠ»ΠΎΠΆΠ½Π΅Π΅. ΠΠ΅Π»ΠΎ Π² ΡΠΎΠΌ, ΡΡΠΎ ΡΠΎΠ·Π΄Π°ΡΠ΅Π»ΠΈ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΈ Adafruit_SSD1306 Π½ΠΈΡΠ΅Π³ΠΎ Π½Π΅ Π·Π½Π°ΡΡ ΠΏΡΠΎ Π‘++ ΠΈΠ½ΡΠ΅ΡΡΠ΅ΠΉΡΡ Π½Π°ΠΏΠΈΡΠ°Π»ΠΈ ΠΊΠΎΠ΄ ΠΈ Π΄Π»Ρ SPI ΠΈ Π΄Π»Ρ I2C. ΠΡΠΈΡΠ΅ΠΌ Π²ΡΠ±ΠΎΡ Π½ΡΠΆΠ½ΠΎΠ³ΠΎ ΠΏΡΠΎΠΈΡΡ
ΠΎΠ΄ΠΈΡ Π² ΡΠ°Π½ΡΠ°ΠΉΠΌΠ΅. ΠΠΎΡΡΠΎΠΌΡ ΠΊΠΎΠΌΠΏΠΈΠ»ΡΡΠΎΡΡ Π½ΠΈΡΠ΅Π³ΠΎ Π½Π΅ ΠΎΡΡΠ°Π΅ΡΡΡ, ΠΊΡΠΎΠΌΠ΅ ΠΊΠ°ΠΊ Π²Π»Π΅ΠΏΠΈΡΡ ΠΎΠ±Π΅ ΡΠ΅Π°Π»ΠΈΠ·Π°ΡΠΈΠΈ Π² ΠΊΠΎΠ΄. Π Π΅ΡΠ°Π΅ΡΡΡ ΡΡΡΡ Π±ΠΎΠ»Π΅Π΅ ΠΈΠ½ΡΠ΅Π»Π»Π΅ΠΊΡΡΠ°Π»ΡΠ½ΡΠΌ ΠΊΠΎΠΌΠΌΠ΅Π½ΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ΠΌ ΠΊΠΎΠ΄Π° Π² Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ΅.
ΠΡΠ΅ ΠΎΡΡΠ°Π»ΡΠ½ΠΎΠ΅ ΠΏΠΎ ΠΌΠ΅Π»ΠΎΡΠΈ. ΠΠ΄Π΅ ΡΠΌΠΎΠ³ β ΠΏΡΠΎΠΏΠ°ΡΡΠΈΠ» Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΈ, ΡΠ°ΡΡΡΠ°Π²ΠΈΠ» const Π³Π΄Π΅ Π½ΡΠΆΠ½ΠΎ. ΠΠΎ Π² ΠΎΡΠ½ΠΎΠ²Π½ΠΎΠΌ ΠΎΡΡΠ°Π²ΠΈΠ» Π²ΡΠ΅ ΠΊΠ°ΠΊ Π΅ΡΡΡ. ΠΠ° Π΄Π°Π½Π½ΡΠΉ ΠΌΠΎΠΌΠ΅Π½Ρ Π·Π°Π½ΡΡΠΎ 55ΠΊΠ± ΡΠ»Π΅ΡΠ°, ΠΈΠ· Π½ΠΈΡ ΠΌΠΎΠ΅Π³ΠΎ ΠΊΠΎΠ΄Π° ΡΡΡΡ ΠΌΠ΅Π½ΡΡΠ΅ 7ΠΊ β Π²ΡΠ΅ ΠΎΡΡΠ°Π»ΡΠ½ΠΎΠ΅ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΈ. ΠΠΎΡ ΡΡΡΡ Π±ΠΎΠ»Π΅Π΅ Π΄Π΅ΡΠ°Π»ΡΠ½ΠΎ, Π΅ΡΠ»ΠΈ ΠΊΠΎΠΌΡ ΠΈΠ½ΡΠ΅ΡΠ΅ΡΠ½ΠΎ
Name | Size |
.text section (Code in ROM) | |
System stuff | 320 |
My code | 212 |
NeoGPS | 4056 |
Adafruit SSD1306 | 3108 |
FreeRTOS | 3452 |
Arduino: Wire Library (I2C) | 296 |
My Code | 6744 |
Board init / system stuff | 788 |
libmaple | 3778 |
Arduino (HardwareSerial, Print) | 1978 |
libmaple | 280 |
libmaple USB CDC | 2216 |
libmaple USB CoreLib | 2388 |
math | 12556 |
libc (malloc/free, memcpy, strcmp) | 3456 |
Total: | 45628 |
.data section (RAM) | |
libmaple constants & tables | 820 |
USB stuff & descriptors (after cleanup) | 84 |
Impure data (WTF? Used in FreeRTOS) | 1068 |
malloc stuff | 1044 |
Total: | 3016 |
.rodata section (constants in ROM) | |
NeoGPS constants | 140 |
Adafruit_SSD1306 constants | 76 |
default font | 1280 |
vtables | 120 |
Monospace8x12 font | 1512 |
vtables | 42 |
My classes data + vtables | 886 |
TimeFont | 528 |
My classes data + vtables | 168 |
Arduino + libmaple stuff | 792 |
USB descriptors | 260 |
Math constants | 552 |
Total: | 6356 |
.bss section (Zeroed variables in RAM) | |
stuff | 28 |
display buffer | 512 |
Heap | 8288 |
FreeRTOS | 192 |
My data | 868 |
libmaple + arduino | 168 |
usb | 548 |
malloc stuff | 56 |
usb | 60 |
Total: | 10720 |
name | Size |
CurrentPositionScreen::drawScreen() const::longtitudeString | 17 |
CurrentPositionScreen::drawScreen() const::latitudeString | 19 |
timeZoneScreen | 12 |
odometer1 | 52 |
odometer0 | 52 |
gpsDataModel | 192 |
odometer2 | 52 |
gpsParser | 292 |
lastPeriodIdleValue | 4 |
curIdleTicks | 4 |
lastCountedTick | 4 |
lastCountedPeriod | 4 |
debugScreen | 12 |
speedScreen | 12 |
positionScreen | 8 |
timeScreen | 12 |
screenStack | 20 |
rootSettingsScreen | 8 |
display | 40 |
satellitesScreen | 12 |
screenIdx | 4 |
odometerScreen | 24 |
altitudeScreen | 8 |
Π‘ΡΠΎΠΈΡ ΠΎΡΠΌΠ΅ΡΠΈΡΡ, ΡΡΠΎ ΡΠ°ΠΌ ΡΠ³Π΅Π½Π΅ΡΠΈΡΠΎΠ²Π°Π½ΡΠΉ ΠΊΠΎΠ΄ ΠΏΠΎΠ»ΡΡΠ°Π΅ΡΡΡ Π΄ΠΎΠ²ΠΎΠ»ΡΠ½ΠΎ ΠΊΠΎΠΌΠΏΠ°ΠΊΡΠ½ΡΠΌ (Ρ ΠΎΡΡ ΠΈ Π±ΠΎΠ»Π΅Π΅ ΡΠ°Π·ΠΌΠ°ΡΠΈΡΡΡΠΌ, ΡΠ΅ΠΌ Π½Π° AVR). Π― Π½Π΅ Π·Π½Π°Ρ Π°ΡΡΠ΅ΠΌΠ±Π»Π΅ΡΠ° ARM, Π½ΠΎ Π²ΡΠ³Π»ΡΠ΄ΠΈΡ ΠΎΠ½ ΡΠ°ΠΊΠΈΠΌ. ΠΠΏΡΠΈΠΌΠΈΠ·Π°ΡΠΎΡ, ΠΊΡΡΠ°ΡΠΈ, Π½Π΅ ΡΠ°ΠΊ Π»ΠΈΡ ΠΎ ΠΏΠ΅ΡΠ΅ΠΌΠ΅ΡΠΈΠ²Π°Π΅Ρ ΠΊΠΎΠ΄ ΠΊΠ°ΠΊ Π² ΡΠ»ΡΡΠ°Π΅ AVR. ΠΡΠ΅ ΡΡΠ½ΠΊΡΠΈΠΈ ΡΠ³ΡΡΠΏΠΏΠΈΡΠΎΠ²Π°Π½Ρ ΠΏΠΎ ΠΈΡ ΠΈΠ·Π½Π°ΡΠ°Π»ΡΠ½ΠΎΠΌΡ ΠΌΠ΅ΡΡΠΎΡΠ°ΡΠΏΠΎΠ»ΠΎΠΆΠ΅Π½ΠΈΡ β ΡΡΠΎ Π·Π½Π°ΡΠΈΡΠ΅Π»ΡΠ½ΠΎ ΠΎΠ±Π»Π΅Π³ΡΠ°Π΅Ρ ΡΡΠ΅Π½ΠΈΠ΅.
Π Π²ΠΎΡ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΡΠ½ΡΠ΅ ΡΡΠ½ΠΊΡΠΈΠΈ libc Π·Π°Π½ΠΈΠΌΠ°ΡΡ Π½Π΅ΠΏΡΠΈΠ»ΠΈΡΠ½ΠΎ ΠΌΠ½ΠΎΠ³ΠΎ. Π― ΡΠΆΠ΅ ΠΏΠΈΡΠ°Π» ΠΏΡΠΎ 12ΠΊ Π½Π° sprintf. ΠΡΠΎ Π΅ΡΠ΅ Π½Π΅ Π²ΡΠ΅. Π€ΡΠ½ΠΊΡΠΈΠΈ ΡΠΈΠΏΠ° strcmp ΠΈΠ»ΠΈ memset Π·Π°Π½ΠΈΠΌΠ°ΡΡ ΠΏΠΎ Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΡ ΡΠΊΡΠ°Π½ΠΎΠ² Π°ΡΡΠ΅ΠΌΠ±Π»Π΅ΡΠ½ΠΎΠ³ΠΎ ΠΊΠΎΠ΄Π°. Π₯ΠΎΡΠ΅Π» Π±Ρ Ρ ΠΏΠΎΡΠΌΠΎΡΡΠ΅ΡΡ ΡΡΠΎ ΠΎΠ½ΠΈ ΡΠ°ΠΌ Π΄Π΅Π»Π°ΡΡ. Π― Π΄Π°ΠΆΠ΅ ΡΠΊΠ°ΡΠ°Π» ΠΈΡΡ ΠΎΠ΄Π½ΠΈΠΊΠΈ newlib, Π³Π΄Π΅ ΡΡΠΈ ΡΡΠ½ΠΊΡΠΈΠΈ ΡΠ΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Π½Ρ. ΠΠΎ ΡΠ°ΠΌ Π½Π° Π°ΡΡΠ΅ΠΌΠ±Π»Π΅ΡΠ΅ ΠΈ Π½Π°ΠΏΠΈΡΠ°Π½Ρ. Π‘ ΠΌΠΈΠ½ΠΈΠΌΡΠΌΠΎΠΌ ΠΊΠΎΠΌΠΌΠ΅Π½ΡΠ°ΡΠΈΠ΅Π². Π’Π°ΠΊ ΡΡΠΎ ΠΏΠΎΠ½ΡΡΠ½Π΅Π΅ Π½Π΅ ΡΡΠ°Π»ΠΎ. ΠΠΎΠΆΠ½ΠΎ Π±ΡΠ»ΠΎ Π±Ρ ΠΏΠ΅ΡΠ΅ΠΏΠΈΡΠ°ΡΡ ΡΠ°ΠΌΠΎΡΡΠΎΡΡΠ΅Π»ΡΠ½ΠΎ, Π½ΠΎ, ΠΏΠΎ ΠΌΠΎΠ΅ΠΌΡ, ΠΏΠ΅ΡΠ΅ΠΏΠΈΡΡΠ²Π°ΡΡ ΡΠ°ΠΊΠΈΠ΅ ΡΡΡΠΊΠΈ ΡΡΠΎ ΠΊΠΎΡΡΠ½ΡΡΠ²ΠΎ.
ΠΠΎΠ»ΡΡΠ΅ Π²ΡΠ΅Π³ΠΎ, ΠΊΠΎΠ½Π΅ΡΠ½ΠΎ, Π·Π°Π½ΠΈΠΌΠ°ΡΡ ΡΡΠΈΠ³ΠΎΠ½ΠΎΠΌΠ΅ΡΡΠΈΡΠ΅ΡΠΊΠΈΠ΅ ΡΡΠ½ΠΊΡΠΈΠΈ ΠΈ ΠΌΠ°ΡΠ΅ΠΌΠ°ΡΠΈΠΊΠ° Ρ ΠΏΠ»Π°Π²Π°ΡΡΠ΅ΠΉ ΡΠΎΡΠΊΠΎΠΉ. ΠΠΎ Π΅ΡΠ»ΠΈ ΡΡΠ΅ΡΡΡ, ΡΡΠΎ Π²ΡΠ΅Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΡΠ΅ ΡΠ°ΡΡΠ΅ΡΡ ΡΡΠΎ ΠΈ Π΅ΡΡΡ ΡΡΡΡ ΠΏΡΠΈΠ±ΠΎΡΠ°, ΡΠΎ ΠΏΡΠΈΠ΄Π΅ΡΡΡ ΡΠΌΠΈΡΠΈΡΡΡΡ.
ΠΠ΄ΠΈΠ½ΡΡΠ²Π΅Π½Π½Π°Ρ ΠΊΡΡΠΏΠ½Π°Ρ ΠΈ Π½Π΅ΠΏΠΎΠ½ΡΡΠ½Π°Ρ Π΄Π»Ρ ΠΌΠ΅Π½Ρ ΡΠ°ΡΡΡ β malloc/free. Π ΡΠ²ΠΎΠ΅ΠΌ ΠΊΠΎΠ΄Π΅ Ρ Π΅Π΅ ΡΠ²Π½ΠΎ Π½Π΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΡ. Π£ FreeRTOS Π΅ΡΡΡ ΡΠ²ΠΎΡ ΡΠ΅Π°Π»ΠΈΠ·Π°ΡΠΈΡ. ΠΡΠΊΡΠ΄Π° ΠΎΠ½ΠΎ Π»Π΅Π·Π΅Ρ Π½Π΅ΡΡΠ½ΠΎ. ΠΡΠ·ΠΎΠ²ΠΎΠ² Ρ Π½Π΅ Π½Π°ΡΠ΅Π». Π― ΠΏΡΠΎΠ±ΠΎΠ²Π°Π» ΠΎΡΠΊΠ°ΡΠΈΡΡΡΡ Π½Π° ΡΠ°ΠΌΡΠΉ ΠΏΠ΅ΡΠ²ΡΠΉ ΠΊΠΎΠΌΠΌΠΈΡ ΠΊΠΎΠ³Π΄Π° Ρ ΡΠΏΠΎΡΡΠΈΡΠΎΠ²Π°Π» ΡΠ²ΠΎΠΉ ΠΏΡΠΎΠ΅ΠΊΡ Π½Π° STM32 β ΡΡΠΎΡ ΠΊΠΎΠ΄ ΡΠΆΠ΅ Π±ΡΠ» Π² ΠΏΡΠΎΡΠΈΠ²ΠΊΠ΅. Π‘ΠΊΠ°ΠΆΡ Π±ΠΎΠ»ΡΡΠ΅. ΠΡΠ»ΠΈ Π² ΠΏΡΡΡΠΎΠΌ ΠΏΡΠΎΠ΅ΠΊΡΠ΅ ΠΏΠΎΠ΄ΠΊΠ»ΡΡΠΈΡΡ Adafruit_GFX ΡΠΆΠ΅ Π±ΡΠ΄Π΅Ρ malloc. ΠΡΡΠ΄ Π»ΠΈ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ° ΡΡΡ Π²ΠΈΠ½ΠΎΠ²Π°ΡΠ° β Ρ ΠΏΠΎΠ΄ΠΊΠ»ΡΡΠ°Π» ΡΠΎΠ²ΡΠ΅ΠΌ Π½Π΅Π²ΠΈΠ½Π½ΡΠΉ Ρ Π΅Π΄Π΅Ρ Ρ ΡΠ°ΠΉΠΏΠ΄Π΅ΡΠ°ΠΌΠΈ. Π‘ΠΊΠΎΡΠ΅Π΅ Π²ΡΠ΅Π³ΠΎ ΡΡΠΎ ΠΊΠ°ΠΊΠΈΠ΅ ΡΠΎ ΠΊΠΎΡΡΠΊΠΈ Π±ΠΈΠ»Π΄ΡΠΈΡΡΠ΅ΠΌΡ.
Π ΠΎΡΡΠ°Π»ΡΠ½ΠΎΠΌ Π²ΡΠ΅ Π²ΡΠ³Π»ΡΠ΄ΠΈΡ Π΄ΠΎΠ²ΠΎΠ»ΡΠ½ΠΎ ΠΏΡΠΈΠ»ΠΈΡΠ½ΠΎ.
ΠΠΎΡΠ»Π΅ΡΠ»ΠΎΠ²ΠΈΠ΅
Π‘ΡΠ°Π²Π»Ρ Π±ΡΡΡΠ»ΠΊΡ ΡΠΎΠΌΡ, ΠΊΡΠΎ Π΄ΠΎΡΠΈΡΠ°Π» Π΄ΠΎ ΡΡΠΎΠ³ΠΎ ΠΌΠ΅ΡΡΠ° (Π‘) ΡΡΡΠ΄Π΅Π½ΡΠ΅ΡΠΊΠ°Ρ Π±Π°ΠΉΠΊΠ°
ΠΡΠΎΠ΅ΠΊΡ ΠΌΠ΅Π΄Π»Π΅Π½Π½ΠΎ, Π½ΠΎ ΡΠ²Π΅ΡΠ΅Π½Π½ΠΎ Π΄Π²ΠΈΠΆΠ΅ΡΡΡ ΠΊ ΡΠ΅Π»ΠΈ. Π ΡΡΠΎΠΉ ΡΠ°ΡΡΠΈ Ρ ΠΏΠ΅ΡΠ΅Π΅Π·ΠΆΠ°Π» Π½Π° Π±ΠΎΠ»Π΅Π΅ ΠΌΠΎΡΠ½ΡΡ ΠΏΠ»Π°ΡΡΠΎΡΠΌΡ ARM/STM32 ΠΈ, Π΅ΡΠ»ΠΈ ΡΠ΅ΡΡΠ½ΠΎ, ΠΌΠ½Π΅ ΡΡΠΎ ΡΠ΅ΡΡΠΎΠ²ΡΠΊΠΈ ΠΏΠΎΠ½ΡΠ°Π²ΠΈΠ»ΠΎΡΡ. ΠΠΎ ΠΏΡΠ΅ΠΆΠ½Π΅ΠΌΡ Π΅ΡΡΡ ΠΌΠ½ΠΎΠ³ΠΎ Π½Π΅Π΄ΠΎΠΏΠΎΠ½ΠΈΠΌΠ°Π½ΠΈΡ ΠΊΠ°ΠΊ Π²ΡΠ΅ ΡΠ°Π±ΠΎΡΠ°Π΅Ρ, Π΄Π°ΡΠ°ΡΠΈΡ ΠΏΡΠΎΡΠΈΡΠ°Π½ ΠΏΡΠΎΡΠ΅Π½ΡΠΎΠ² Π½Π° 20. ΠΠΎ ΡΡΠΎ ΡΠΎΠ²Π΅ΡΡΠ΅Π½Π½ΠΎ Π½Π΅ ΠΌΠ΅ΡΠ°Π΅Ρ Π΄Π²ΠΈΠ³Π°ΡΡΡΡ Π΄Π°Π»ΡΡΠ΅.
ΠΡΠ΅ ΠΎΠ΄ΠΈΠ½ ΠΊΡΡΠΏΠ½ΡΠΉ ΡΠ°Π³, ΠΊΠΎΡΠΎΡΡΠΉ Ρ ΡΠ΄Π΅Π»Π°Π» β ΠΏΠ΅ΡΠ΅Π΅Π·Π΄ Π½Π° FreeRTOS. ΠΠΎΠ΄ ΡΡΠ°Π» ΡΡΡΠ΅ΡΡΠ²Π΅Π½Π½ΠΎ ΠΏΡΠΎΡΠ΅ ΠΈ Π±ΠΎΠ»Π΅Π΅ ΡΡΡΡΠΊΡΡΡΠΈΡΠΎΠ²Π°Π½Π½ΡΠΌ. Π ΡΠ°ΠΌΠΎΠ΅ Π³Π»Π°Π²Π½ΠΎΠ΅ Π΅Π³ΠΎ Π»Π΅Π³ΠΊΠΎ ΡΠ°ΡΡΠΈΡΡΡΡ Π΄Π°Π»ΡΡΠ΅.
ΠΠ°ΠΊΠΎΠ½Π΅Ρ Ρ ΠΏΠΎΠ΄ΠΊΠ»ΡΡΠΈΠ» GPS ΠΏΡΠΈΠ΅ΠΌΠ½ΠΈΠΊ. Π‘ ΠΏΠΎΠΌΠΎΡΡΡ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΈ NeoGPS Ρ ΡΠΌΠΎΠ³ ΠΏΠΎΠ»ΡΡΠΈΡΡ Π²ΡΠ΅ Π½ΡΠΆΠ½ΡΠ΅ Π΄Π°Π½Π½ΡΠ΅ ΠΈ ΠΎΡΠΎΠ±ΡΠ°Π·ΠΈΡΡ ΠΈΡ Π½Π° ΡΠΎΠΎΡΠ²Π΅ΡΡΡΠ²ΡΡΡΠΈΡ ΡΠΊΡΠ°Π½Π°Ρ . ΠΡΠΈΡΠ»ΠΎΡΡ, ΠΏΡΠ°Π²Π΄Π°, ΠΏΠΎΠ²ΠΎΠ·ΠΈΡΡΡ Ρ ΠΈΠ·ΠΎΠ±ΡΠ΅ΡΠ΅Π½ΠΈΠ΅ΠΌ Π²Π½ΡΡΡΠ΅Π½Π½Π΅ΠΉ ΠΌΠΎΠ΄Π΅Π»ΠΈ Π΄Π°Π½Π½ΡΡ .
Π‘Π΅ΠΉΡΠ°Ρ Ρ ΡΠΏΠ΅ΡΡΡ Π² ΠΏΡΠΎΠ±Π»Π΅ΠΌΡ Ρ Π±ΠΈΠ»Π΄ ΡΠΈΡΡΠ΅ΠΌΠΎΠΉ Π°ΡΠ΄ΡΠΈΠ½ΠΎ. ΠΠ½Π° Ρ ΠΎΡΠΎΡΠ° Π΄Π»Ρ ΠΌΠ΅Π»ΠΊΠΈΡ ΠΏΡΠΎΠ΅ΠΊΡΠΎΠ², Π½ΠΎ ΠΌΠ½Π΅ ΠΎΠ½Π° ΠΆΠΌΠ΅Ρ Π±ΡΠΊΠ²Π°Π»ΡΠ½ΠΎ ΡΠΎ Π²ΡΠ΅Ρ ΡΡΠΎΡΠΎΠ½. Π‘ΠΈΡΡΠ΅ΠΌΠ° ΠΏΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΈ Π½Π΅ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠΈΡΡΠ΅ΡΡΡ ΠΈ ΠΌΠ½ΠΎΠ³ΠΈΠ΅ Π²Π΅ΡΠΈ ΠΏΡΠΎΠΈΡΡ ΠΎΠ΄ΡΡ Π±Π΅Π· ΠΌΠΎΠ΅Π³ΠΎ Π²Π΅Π΄ΠΎΠΌΠ°. Π ΡΠΎΠΌΡ ΠΆΠ΅ Ρ ΠΌΠ΅Π½Ρ ΠΎΡΠ΅Π½Ρ ΠΌΠ½ΠΎΠ³ΠΎ Π²ΠΎΠΏΡΠΎΡΠΎΠ² ΡΠΎ ΡΡΠΎΡΠΎΠ½Ρ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΎΠ½Π½ΠΎΠ³ΠΎ ΠΌΠ΅Π½Π΅Π΄ΠΆΠΌΠ΅Π½ΡΠ°: ΠΊΠ°ΠΊ Π²Π΅ΡΡΠΈΠΎΠ½ΠΈΡΠΎΠ²Π°ΡΡ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΡ Π² Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠ°Ρ ? ΠΊΠ°ΠΊ ΡΠ°Π·Π»ΠΎΠΆΠΈΡΡ ΡΠ²ΠΎΠΈ ΠΈΡΡ ΠΎΠ΄Π½ΠΈΠΊΠΈ ΠΏΠΎ Π΄ΠΈΡΠ΅ΠΊΡΠΎΡΠΈΡΠΌ, ΡΡΠΎΠ±Ρ ΡΡΠΎ Π±ΡΠ»ΠΎ ΡΠ΄ΠΎΠ±Π½ΠΎ? ΠΠ°ΠΊ Π·Π°Π»ΠΈΡΡ ΡΡΠΎ Π² ΡΠ΅ΠΏΠΎΠ·ΠΈΡΠΎΡΠΈΠΉ, ΡΡΠΎΠ±Ρ ΡΠΎΡΠ°ΡΠ½ΠΈΠΊΠ°ΠΌ ΠΌΠΎΠΆΠ½ΠΎ Π±ΡΠ»ΠΎ Ρ ΡΡΠΈΠΌ ΡΠ°Π±ΠΎΡΠ°ΡΡ? Π ΠΎΠ±ΡΠ΅ΠΌ ΡΡΠΎ Π±ΡΠ΄Π΅Ρ ΠΏΠ΅ΡΠ²ΡΠΉ ΠΏΡΠΈΠΎΡΠΈΡΠ΅Ρ Π² Π΄Π°Π»ΡΠ½Π΅ΠΉΡΠ΅ΠΉ ΡΠ°Π±ΠΎΡΠ΅.
Π’ΠΎΠ»ΡΠΊΠΎ ΡΡΠ΅Ρ ΠΌΠΎΡ ΡΡΠΉΠΊΠ°, ΡΡΠΎ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ Π±ΠΈΠ»Π΄ ΡΠΈΡΡΠ΅ΠΌΡ ΠΏΠΎΠ²Π»Π΅ΡΠ΅Ρ Π·Π° ΡΠΎΠ±ΠΎΠΉ ΠΈ Π΄ΡΡΠ³ΠΈΠ΅ Π²Π΅ΡΠΈ. ΠΠΎ Π²ΡΠ΅ΠΉ Π²ΠΈΠ΄ΠΈΠΌΠΎΡΡΠΈ ΠΏΡΠΈΠ΄Π΅ΡΡΡ ΠΏΠ΅ΡΠ΅Π΅Ρ Π°ΡΡ Ρ Atmel Studio Π½Π° CooCox ΠΈΠ»ΠΈ Π΄ΡΡΠ³ΡΡ IDE. ΠΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΠΌΠ΅Π½ΡΠ΅ΡΡΡ ΠΊΠΎΠΌΠΏΠΈΠ»ΡΡΠΎΡ. ΠΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ ΠΏΡΠΈΠ΄Π΅ΡΡΡ ΠΎΡΠΊΠ°Π·ΡΠ²Π°ΡΡΡΡ ΠΎΡ ΡΡΠ΅ΠΉΠΌΠ²ΠΎΡΠΊΠ° Arduino. ΠΠΎΠΊΠ° ΡΠ»ΠΎΠΆΠ½ΠΎ ΡΠΊΠ°Π·Π°ΡΡ ΡΡΠΎ ΠΎΠ½ΠΎ Π·Π° ΡΠΎΠ±ΠΎΠΉ ΠΏΠΎΡΡΠ½Π΅Ρ.
ΠΡ Π° ΠΏΠΎΡΠΎΠΌ Π±ΡΠ΄Π΅Ρ ΠΏΠΎΠ΄ΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅ SD ΠΊΠ°ΡΡΡ, ΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΠ΅ ΠΏΠΈΡΠ°Π½ΠΈΠ΅ΠΌ, USB Mass Storage Device ΠΈ ΠΌΠ½ΠΎΠ³ΠΎ Π²ΡΠ΅Π³ΠΎ ΠΈΠ½ΡΠ΅ΡΠ΅ΡΠ½ΠΎΠ³ΠΎ.
ΠΡΠ»ΠΈ ΠΊΠΎΠΌΡ ΠΏΠΎΠ½ΡΠ°Π²ΠΈΠ»ΠΎΡΡ β ΠΏΡΠΈΠ³Π»Π°ΡΠ°Ρ ΠΏΡΠΈΡΠΎΠ΅Π΄ΠΈΠ½ΠΈΡΡΡ ΠΊ ΠΏΡΠΎΠ΅ΠΊΡΡ. Π― ΡΠ°ΠΊΠΆΠ΅ Π±ΡΠ΄Ρ ΡΠ°Π΄ ΠΊΠΎΠ½ΡΡΡΡΠΊΡΠΈΠ²Π½ΡΠΌ ΠΊΠΎΠΌΠΌΠ΅Π½ΡΠ°ΡΠΈΡΠΌ β ΠΎΠ½ΠΈ ΠΌΠ½Π΅ ΠΎΡΠ΅Π½Ρ ΠΏΠΎΠΌΠΎΠ³Π°ΡΡ.
β Π‘ΡΡΠ°Π½ΠΈΡΠΊΠ° ΠΏΡΠΎΠΊΡΠ° Π½Π° Π³ΠΈΡΡ Π°Π±Π΅