-
Notifications
You must be signed in to change notification settings - Fork 0
Software
Finally we get to the aspect of this project, which is the reason, I decided to upload the project here. I am going to explain what libraries I included in my project, where and why they are used, and then walk you through my code. It is not complex in any means, but I would like to explain my thought process behind it and the choices I made.
I used 3 software libraries for the project to ease my communication via SPI protocol(NRF24L01 transceiver modules) and to manipulate the Arduino timers.
This library enables communication between Arduino (master) with Serial Peripheral Interface (SPI) devices, in our case NRF2401L (slave). The data protocol uses three pins: MISO (Master In Slave Out), MOSI (Master Out Slave In) and SCK (Serial Clock). This library is included in order to make the RF24 library work.
The mentioned library eases our work with NRF2401L+ communicating via SPI protocol. It is very well documented and supports various features for beginners and advanced users. I wanted to be able to:
- send the commands for the throttle/break and 2 lights from the remote control to the longboard
- receive the battery voltage from the longboard on the remote to display it to the user
I was able to send all 3 needed parameters in a single payload using C data structures, then I just needed a way to receive the battery voltage level. A basic example for communication between two transceivers shows how they alternately switch transmitter and receiver roles to exchange information, but there is a more efficient way. The library allows us to write a custom payload to an autoACK response, and therefore enables faster call-response communication. When the longboard transceiver receives a payload with the required values, it sends an autoACK in which it includes the current battery voltage level.
I used the PWM library to modify the frequency of the Timer1 hardware timer on the Arduino. This allows for more precise 50 Hz PWM signal, with the 1-2 ms duty cycle, needed to send the throttle/break values to the FOCBOX speed controller. But the number of states between 0 and 100% duty cycle (resolution) depends on the frequency the timer operates on. The pwmWriteHR() function accepts a 16 bit integer (the reason I used 6554 for max throttle and 3277 for max break in my sketches), but it will automatically map to any timer resolution. The timer resolution depends on the given frequency. At 50 Hz we get about 11 bits of actual resolution.
When the signal from the remote control is received the data is read and the throttle and lights are adjusted accordingly. The board battery level is sent as an ACK response. If no signal is received for more than a half of second, the board throttle is set to neutral and the lights turn off.
Right before the loop() function the PWM duty cycle is set to neutral so the board stays still after boot.
The loop() void listens for incoming radio signal from the remote and when the signal is caught it is saved as a data structure, simply called "data". The content of the struct is then checked to make sure the received signal matches the expected values. If the condition is met the values for throttle/break are re-mapped to the range between the calculated full-break to full-throttle values. Those values are then used by the PWM library to generate the corresponding PWM signal for the FOCBOX.
The lightsControl() function is used to check, if any of the lights need to be turned on/off. If the throttle/break value is lower than the neutral variable value, meaning the user is breaking, the breaking light is turned on. It is also checked if user turned on/off any of the lights manually.
Every time the values are received successfully, the time of completion is recorded and used to check the elapsed time between successful transmissions. If the signal is lost or jammed for more than 500ms, the boards sets throttle to neutral, the lights are turned off and the transceiver is reset in order to try to reconnect to the remote control.
After every successful transmission, the voltage at the analog input, connected to the voltage divider, is read, to determine the voltage of the skateboard battery pack and the value is then sent as a custom ACK payload to the remote control.
Every 20 ms remote control user inputs are read. The values are written to the data structure "data" and sent to the receiver. It is then expected to receive an ACK response from the board, with the battery pack voltage as a custom payload. The voltage is displayed on the remote control using 4 LEDs. If the connection to the board is lost, a indicator LED lights up. The remote also features a safety switch, which need to be held down in order to read the throttle joystick input. If the button is released the throttle is set to neutral.
The readThrottle() is used to read the position of the throttle joystick. If the safety button ("Z") is pressed the analog value from the throttle joystick is read. The code than checks if the safety button was pressed before the joystick was pushed in order the board from accelerating with full speed from stop. The joystick value is then written to the custom data structure for transmission.
The setNeutral() is an interrupt function, triggered by the falling edge of the safety button ("Z") input signal. It sets the throttle value to neutral.
The readLightButton() is used to check the "C" button state. Every time the button is pressed, another lights combination is chosen. User can choose between 3 combinations: only back light on, both lights on and both lights off.
The readBattery() displays the battery level received from the custom ACK payload from the board. 4 lights on the remote control represent different voltage levels: green(>40 V), yellow(>37 V), orange(>35 V), red(<35 V).