# Nucleo-144 (F412ZG) ### 1. Use Arduino IDE to program ![](https://i.imgur.com/oOmf3R4.jpg) ![](https://i.imgur.com/6eoE52p.jpg) --- ### 2. Pinout - ==Zio and Arduino-compatible headers== ![](https://i.imgur.com/fsC7F6j.png) ![](https://i.imgur.com/Ibwg7hB.png) ==CN11 CN12 headers== ![](https://i.imgur.com/8jBAGzV.png) ![](https://i.imgur.com/W5DrSVY.png) --- ### 3. Prequirmental libraries Timer action libraries (附上github連結就好) 中斷計時 ... --- ### 4. ROS and onbeded leds+buttons testing code ==Create "nucleo.launch" on ROS-Master== ``` javascript= <launch> <node pkg="rosserial_python" type="serial_node.py" name="nucleo" > <param name="port" value="/dev/ttyACM0" /> <param name="baud" value="57600" /> </node> </launch> ``` ==on Nucleo-144== ``` javascript= #include <SPI.h> #include <ros.h> #include <ros/time.h> #include <std_msgs/Float64.h> // ROS setting ros::NodeHandle nh; std_msgs::Float64 nucleo_msg; ros::Publisher pub_nucleo("nucleo_data", &nucleo_msg); int buttonState = 0; // variable for reading the pushbutton status void setup() { Serial.begin(57600); delay(1000); // ROS node nh.initNode(); nh.advertise(pub_nucleo); pinMode(PB0, OUTPUT); // User led1 pinMode(PB7, OUTPUT); // User led2 pinMode(PB14, OUTPUT); // User led3 pinMode(PC13, INPUT); // User button nucleo_msg.data = 64; } // the loop function runs over and over again forever void loop() { buttonState = digitalRead(PC13); // check if the pushbutton is pressed. If it is, the buttonState is HIGH: if (buttonState == HIGH) { // turn LED on: digitalWrite(PB0, HIGH); digitalWrite(PB7, HIGH); digitalWrite(PB14, HIGH); nucleo_msg.data +=1; } else { // turn LED off: digitalWrite(PB0, LOW); digitalWrite(PB7, LOW); digitalWrite(PB14, LOW); } pub_nucleo.publish(&nucleo_msg); nh.spinOnce(); } ``` ==on Terminals== ```javascript= roscore roslaunch pev_sensors nucleo.launch rostopic echo /nucleo_data ``` ==Result: Push the button and the led1-led3 will be on and the number increase.== ![](https://i.imgur.com/iW21dSe.jpg) ![](https://i.imgur.com/FCmWqfu.jpg) ![](https://i.imgur.com/5VMRTQb.png) --- ### 5. ROS and Neopixel code ==on Nucleo-144== :::info :bulb: **TODO:** Rainbow在使用neopixel_library for Nucleo-144有bug, **修復Rainbow-mode** ::: ``` javascript= #include <Timer.h> #include <ros.h> #include <std_msgs/String.h> #include <std_msgs/ColorRGBA.h> #include <Adafruit_NeoPixel.h> #ifdef __AVR__ #include <avr/power.h> #endif #define PIN_FRONT_LEFT D7 #define PIN_FRONT_RIGHT D8 #define PIN_TOP_LEFT D9 #define PIN_TOP_RIGHT D10 #define PIXEL_FRONT_LEFT 92 #define PIXEL_FRONT_RIGHT 91 #define PIXEL_TOP_LEFT 100 #define PIXEL_TOP_RIGHT 100 #define LED_RATE_ 20 // period in ms #define SHOW_COLOR_RATE_ 20 // period in ms #define UPDATE_RGB_RATE_ 15 // Functions void frontCb( const std_msgs::ColorRGBA& rec_msg); void topCb( const std_msgs::ColorRGBA& rec_msg); void initial_neopixel(void); void update_rgb(void* context); void show_color(void* context); void fillAll(Adafruit_NeoPixel &strip_l, Adafruit_NeoPixel &strip_r, uint32_t c); void colorWipe(Adafruit_NeoPixel &strip_l, Adafruit_NeoPixel &strip_r, uint32_t c, long &count); void colorBlink(Adafruit_NeoPixel &strip_l, Adafruit_NeoPixel &strip_r, uint32_t c, long &count, bool &flag); void breathAll(Adafruit_NeoPixel &strip_l, Adafruit_NeoPixel &strip_r, std_msgs::ColorRGBA &rgba, long &count, bool &flag); void snakeScroll(Adafruit_NeoPixel &strip_l, Adafruit_NeoPixel &strip_r, uint32_t c, long &count, bool &flag); void segmentLight(Adafruit_NeoPixel &strip_l, Adafruit_NeoPixel &strip_r, uint32_t c, uint16_t start_pixel, uint16_t end_pixel, bool &clear_flag); void rainbow(Adafruit_NeoPixel &strip_l, Adafruit_NeoPixel &strip_r, std_msgs::ColorRGBA &rgba, long &firstPixelHue); void monoChase(Adafruit_NeoPixel &strip_l, Adafruit_NeoPixel &strip_r, std_msgs::ColorRGBA &rgba, long &count, bool &flag); void leftTurn(Adafruit_NeoPixel &strip_l, Adafruit_NeoPixel &strip_r, uint32_t c); void rightTurn(Adafruit_NeoPixel &strip_l, Adafruit_NeoPixel &strip_r, uint32_t c); // Parameter bool blink_flag_front = true; bool blink_flag_top = true; bool snake_flag_front = true; bool snake_flag_top = true; bool breath_flag_front = true; bool breath_flag_top = true; long count_front = 0; long count_top = 0; long pixelhue_front = 0; long pixelhue_top = 0; // Parameter 1 = number of pixels in strip // Parameter 2 = Arduino pin number (most are valid) // Parameter 3 = pixel type flags, add together as needed: // NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs) // NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2) Adafruit_NeoPixel strip_FRONT_LEFT = Adafruit_NeoPixel(PIXEL_FRONT_LEFT, PIN_FRONT_LEFT, NEO_GRB + NEO_KHZ800); Adafruit_NeoPixel strip_FRONT_RIGHT = Adafruit_NeoPixel(PIXEL_FRONT_RIGHT, PIN_FRONT_RIGHT, NEO_GRB + NEO_KHZ800); Adafruit_NeoPixel strip_TOP_LEFT = Adafruit_NeoPixel(PIXEL_TOP_LEFT, PIN_TOP_LEFT, NEO_GRB + NEO_KHZ800); Adafruit_NeoPixel strip_TOP_RIGHT = Adafruit_NeoPixel(PIXEL_TOP_RIGHT, PIN_TOP_RIGHT, NEO_GRB + NEO_KHZ800); // IMPORTANT: To reduce NeoPixel burnout risk, add 1000 uF capacitor across // pixel power leads, add 300 - 500 Ohm resistor on first pixel's data input // and minimize distance between Arduino and first pixel. Avoid connecting // on a live circuit...if you must, connect GND first. // Timer Timer t_update_rgb; Timer t_show_color; // ROS messages std_msgs::ColorRGBA front_rgba_, top_rgba_; std_msgs::String str_msg; // ROS setup ros::NodeHandle nh; ros::Subscriber<std_msgs::ColorRGBA> sub_strip_front("strip_front", &frontCb); ros::Subscriber<std_msgs::ColorRGBA> sub_strip_top("strip_top", &topCb); //ros::Publisher chatter("chatter", &str_msg); //char hello[] = "hello world!"; //char looper[] = "looper!"; //char cstr[] = ""; void setup() { // This is for Trinket 5V 16MHz, you can remove these three lines if you are not using a Trinket #if defined (__AVR_ATtiny85__) if (F_CPU == 16000000) clock_prescale_set(clock_div_1); #endif // End of trinket special code //Initialize all the led on the breath mode. initial_neopixel(); // LED mode choice and color setting update t_update_rgb.every(UPDATE_RGB_RATE_, update_rgb, 0); t_show_color.every(SHOW_COLOR_RATE_, show_color, 0); // * Init the ROS nh.initNode(); nh.subscribe(sub_strip_front); nh.subscribe(sub_strip_top); // nh.advertise(chatter); // str_msg.data = hello; // chatter.publish( &str_msg ); } void loop() { //initial_neopixel(); t_update_rgb.update(); t_show_color.update(); nh.spinOnce(); } void frontCb( const std_msgs::ColorRGBA& rec_msg){ // once any ros parameters changed, reset the "count_front "/ "count_top" if(rec_msg.a != front_rgba_.a || rec_msg.r != front_rgba_.r || rec_msg.g != front_rgba_.g || rec_msg.b != front_rgba_.b){ count_front = 0; } front_rgba_ = rec_msg; // // str_msg.data = "front_cb"; // chatter.publish( &str_msg ); } void topCb( const std_msgs::ColorRGBA& rec_msg){ if(rec_msg.a != top_rgba_.a || rec_msg.r != top_rgba_.r || rec_msg.g != top_rgba_.g || rec_msg.b != top_rgba_.b){ count_top = 0; } top_rgba_ = rec_msg; // // str_msg.data = "top_cb"; // chatter.publish( &str_msg ); } void initial_neopixel(void){ front_rgba_.a = top_rgba_.a = 1; front_rgba_.r = 0; front_rgba_.g = 100; front_rgba_.b = 0; top_rgba_.r = 10; top_rgba_.g = 10; top_rgba_.b = 10; // Init all the strips strip_FRONT_LEFT.begin(); strip_FRONT_RIGHT.begin(); strip_TOP_LEFT.begin(); strip_TOP_RIGHT.begin(); fillAll(strip_FRONT_LEFT, strip_FRONT_RIGHT, strip_FRONT_LEFT.Color(front_rgba_.r, front_rgba_.g, front_rgba_.b)); fillAll(strip_TOP_LEFT, strip_TOP_RIGHT, strip_TOP_LEFT.Color(top_rgba_.r, top_rgba_.g, top_rgba_.b)); strip_FRONT_LEFT.show(); strip_FRONT_RIGHT.show(); strip_TOP_LEFT.show(); strip_TOP_RIGHT.show(); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void update_rgb(void* context){ // Depending on the mode to set the front strips switch((int) front_rgba_.a) { case 1: fillAll(strip_FRONT_LEFT, strip_FRONT_RIGHT, strip_FRONT_LEFT.Color(front_rgba_.r, front_rgba_.g, front_rgba_.b)); break; case 2: colortWipe(strip_FRONT_LEFT, strip_FRONT_RIGHT, strip_FRONT_LEFT.Color(front_rgba_.r, front_rgba_.g, front_rgba_.b), count_front); break; case 3: colorBlink(strip_FRONT_LEFT, strip_FRONT_RIGHT, strip_FRONT_LEFT.Color(front_rgba_.r, front_rgba_.g, front_rgba_.b), count_front, blink_flag_front); break; case 4: snakeScroll(strip_FRONT_LEFT, strip_FRONT_RIGHT, strip_FRONT_LEFT.Color(front_rgba_.r, front_rgba_.g, front_rgba_.b), count_front, snake_flag_front); break; case 5: breathAll(strip_FRONT_LEFT, strip_FRONT_RIGHT, front_rgba_, count_front, breath_flag_front); break; case 6: rainbow(strip_FRONT_LEFT, strip_FRONT_RIGHT, front_rgba_, count_front); break; case 7: leftTurn(strip_FRONT_LEFT, strip_FRONT_RIGHT, strip_FRONT_LEFT.Color(front_rgba_.r, front_rgba_.g, front_rgba_.b)); break; case 8: rightTurn(strip_FRONT_LEFT, strip_FRONT_RIGHT, strip_FRONT_LEFT.Color(front_rgba_.r, front_rgba_.g, front_rgba_.b)); break; case 9: monoChase(strip_FRONT_LEFT, strip_FRONT_RIGHT, front_rgba_, count_front, snake_flag_front); break; default: break; } // Depending on the mode to set the top strips switch((int) top_rgba_.a) { case 1: fillAll(strip_TOP_LEFT, strip_TOP_RIGHT, strip_TOP_LEFT.Color(top_rgba_.r, top_rgba_.g, top_rgba_.b)); break; case 2: colortWipe(strip_TOP_LEFT, strip_TOP_RIGHT, strip_TOP_LEFT.Color(top_rgba_.r, top_rgba_.g, top_rgba_.b), count_top); break; case 3: colorBlink(strip_TOP_LEFT, strip_TOP_RIGHT, strip_TOP_LEFT.Color(top_rgba_.r, top_rgba_.g, top_rgba_.b), count_top, blink_flag_top); break; case 4: snakeScroll(strip_TOP_LEFT, strip_TOP_RIGHT, strip_TOP_LEFT.Color(top_rgba_.r, top_rgba_.g, top_rgba_.b), count_top, snake_flag_top); break; case 5: breathAll(strip_TOP_LEFT, strip_TOP_RIGHT, top_rgba_, count_top, breath_flag_top); break; case 6: rainbow(strip_TOP_LEFT, strip_TOP_RIGHT, top_rgba_, count_top); break; case 7: leftTurn(strip_TOP_LEFT, strip_TOP_RIGHT, strip_TOP_LEFT.Color(top_rgba_.r, top_rgba_.g, top_rgba_.b)); break; case 8: rightTurn(strip_TOP_LEFT, strip_TOP_RIGHT, strip_TOP_LEFT.Color(top_rgba_.r, top_rgba_.g, top_rgba_.b)); break; case 9: monoChase(strip_TOP_LEFT, strip_TOP_RIGHT, top_rgba_, count_top, snake_flag_top); break; default: break; } } // Show all the color setting void show_color(void* context){ strip_FRONT_LEFT.show(); strip_FRONT_RIGHT.show(); strip_TOP_LEFT.show(); strip_TOP_RIGHT.show(); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////// // Set all the dots with a color void fillAll(Adafruit_NeoPixel &strip_l, Adafruit_NeoPixel &strip_r, uint32_t c) { for(uint16_t i=0; i<strip_l.numPixels(); i++) { strip_l.setPixelColor(i, c); strip_r.setPixelColor(i, c); } } // Set left-side with a color void leftTurn(Adafruit_NeoPixel &strip_l, Adafruit_NeoPixel &strip_r, uint32_t c) { fillAll(strip_l, strip_r, 0); for(uint16_t i=0; i<strip_l.numPixels(); i++) { strip_l.setPixelColor(i, c); } } // Set right-side with a color void rightTurn(Adafruit_NeoPixel &strip_l, Adafruit_NeoPixel &strip_r, uint32_t c) { fillAll(strip_l, strip_r, 0); for(uint16_t i=0; i<strip_l.numPixels(); i++) { strip_r.setPixelColor(i, c); } } // Fill the dots one after the other with a color void colortWipe(Adafruit_NeoPixel &strip_l, Adafruit_NeoPixel &strip_r, uint32_t c, long &count) { if(count >= 0){ if(count > strip_l.numPixels() || count > strip_r.numPixels()){ count=0; } strip_l.setPixelColor(count, c); strip_r.setPixelColor(count, c); count += 1; } } // Toggle all the dots with a color void colorBlink(Adafruit_NeoPixel &strip_l, Adafruit_NeoPixel &strip_r, uint32_t c, long &count, bool &flag) { if (flag){ if (count ==0){ fillAll(strip_l, strip_r, c); count += 1; } else if (count > 0 && count <= 50){ count += 1; } else{ flag = false; count = 0; } // str_msg.data = "set color"; // chatter.publish( &str_msg ); } else { if (count ==0){ fillAll(strip_l, strip_r, 0); count += 1; } else if (count > 0 && count <= 50){ count += 1; } else{ flag = true; count = 0; } // str_msg.data = "clear color!"; // chatter.publish( &str_msg ); } } // Scroll a specific number of dots void snakeScroll(Adafruit_NeoPixel &strip_l, Adafruit_NeoPixel &strip_r, uint32_t c, long &count, bool &flag) { float dots_number = 6; bool clear_flag = true; if(flag){ if((count + dots_number) >= strip_l.numPixels() || (count + dots_number) >= strip_r.numPixels()) { count = strip_l.numPixels() - dots_number; flag = false; } else { segmentLight(strip_l, strip_r, c, count, count + dots_number, clear_flag); count += 1; } } else { if(count < 1) { flag = true; count = 0; } else { segmentLight(strip_l, strip_r, c, count, count + dots_number, clear_flag); count -= 1; } } } void segmentLight(Adafruit_NeoPixel &strip_l, Adafruit_NeoPixel &strip_r, uint32_t c, uint16_t start_pixel, uint16_t end_pixel, bool &clear_flag) { if (clear_flag){ fillAll(strip_l, strip_r, 0); } for(uint16_t i=start_pixel; i<end_pixel; i++) { strip_l.setPixelColor(i, c); strip_r.setPixelColor(i, c); } } // Breathe all the dots void breathAll(Adafruit_NeoPixel &strip_l, Adafruit_NeoPixel &strip_r, std_msgs::ColorRGBA &rgba, long &count, bool &flag) { float frequency = 1; //static double count = 0; static uint8_t max_brightness = 200; static double increase = (float) 10 / 1000.0 * frequency * max_brightness * 2.0; static double r_ratio, g_ratio, b_ratio; r_ratio = rgba.r / max_brightness; g_ratio = rgba.g / max_brightness; b_ratio = rgba.b / max_brightness; if(flag){ if(count >= max_brightness) { flag= false; } else { fillAll(strip_l, strip_r, strip_l.Color(count*r_ratio, count*g_ratio, count*b_ratio)); count += increase; } } else { if(count < 1) { flag = true; } else { fillAll(strip_l, strip_r, strip_l.Color(count*r_ratio, count*g_ratio, count*b_ratio)); count -= increase; } } } // Rainbow cycle along whole strip. Pass delay time (in ms) between frames. void rainbow(Adafruit_NeoPixel &strip_l, Adafruit_NeoPixel &strip_r, std_msgs::ColorRGBA &rgba, long &firstPixelHue) { // Hue of first pixel runs 5 complete loops through the color wheel. // Color wheel has a range of 65536 but it's OK if we roll over, so // just count from 0 to 5*65536. Adding 256 to firstPixelHue each time // means we'll make 5*65536/256 = 1280 passes through this outer loop: if(firstPixelHue >= 0 && firstPixelHue < 65536){ for(int i=0; i<strip_l.numPixels() || i<strip_r.numPixels(); i++) { // For each pixel in strip... // Offset pixel hue by an amount to make one full revolution of the // color wheel (range of 65536) along the length of the strip // (strip.numPixels() steps): int pixelHue = firstPixelHue + (i * 65536L / strip_l.numPixels()); //strip_l.setPixelColor(i, strip_l.gamma32(strip_l.ColorHSV(pixelHue,200,90))); //strip_r.setPixelColor(i, strip_r.gamma32(strip_r.ColorHSV(pixelHue,200,90))); // strip.ColorHSV() can take 1 or 3 arguments: a hue (0 to 65535) or // optionally add saturation and value (brightness) (each 0 to 255). // Here we're using just the single-argument hue variant. The result // is passed through strip.gamma32() to provide 'truer' colors // before assigning to each pixel: } firstPixelHue += 256; if(firstPixelHue >= 65536){ firstPixelHue = 0; } } } void monoChase(Adafruit_NeoPixel &strip_l, Adafruit_NeoPixel &strip_r, std_msgs::ColorRGBA &rgba, long &count, bool &flag){ float dots_number = 6; bool clear_flag = false; int colorMultiple = 15; std_msgs::ColorRGBA ratio; ratio.r = rgba.r / 10; ratio.g = rgba.g / 10; ratio.b = rgba.b / 10; float max_ratio = 255 / (max( max(rgba.r, rgba.g), rgba.r)); fillAll(strip_l,strip_r,strip_l.Color(rgba.r, rgba.g, rgba.b)); if(flag){ if((count + dots_number) >= strip_l.numPixels() || (count + dots_number) >= strip_r.numPixels()) { count = strip_l.numPixels() - dots_number; flag = false; } else { //segmentLight(strip_l, strip_r, strip_l.Color(rgba.r+(ratio.r*colorMultiple), rgba.g+(ratio.g*colorMultiple), rgba.b+(ratio.b*colorMultiple)), count, count + dots_number, clear_flag); segmentLight(strip_l, strip_r, strip_l.Color(rgba.r*max_ratio, rgba.g*max_ratio, rgba.b*max_ratio), count, count + dots_number, clear_flag); count += 1; } } else { if(count < 1) { flag = true; count = 0; } else { //segmentLight(strip_l, strip_r, strip_l.Color(rgba.r+(ratio.r*colorMultiple), rgba.g+(ratio.g*colorMultiple), rgba.b+(ratio.b*colorMultiple)), count, count + dots_number, clear_flag); segmentLight(strip_l, strip_r, strip_l.Color(rgba.r*max_ratio, rgba.g*max_ratio, rgba.b*max_ratio), count, count + dots_number, clear_flag); // ros_popout(String(rgba.r*max_ratio)); // ros_popout(String(rgba.g*max_ratio)); // ros_popout(String(rgba.b*max_ratio)); count -= 1; } } } //void ros_popout(String str){ // //String str = String(firstPixelHue); // str.toCharArray(cstr,16); // str_msg.data = cstr; // chatter.publish( &str_msg ); //} ``` --- ### 6. ROS Topic ==std_msgs::ColorRGBA== R [0:255] + G [0:255] + B [0:255] + A [ custom mode ] | custom mode | name | Column 3 | | ----------- | ----------- | -------- | | 1 | fillAll | Text | | 2 | colorWipe | Text | | 3 | colorBlink | Text | | 4 | snakeScroll | Text | | 5 | breathAll | Text | | 6 | rainbow | Text | | 7 | Text | Text | ==Topics== | topic name | board | pixels | | ------------ | ----------- | ---------- | | strip_front | A1_arduino | 91 + 92 | | strip_top | A1_arduino | 100 + 100 | | strip_lidar | A2_arduino | 50 | | strip_bottom | A2_arduino | 54 + 54 | | strip_back | A3_arduino | 157 | | strip_eye | A3_arduino | 20 + 20 | | stop_lamp | A6_arduino | (GPIO) | :::info :bulb: **TODO:** 整合A1+A2+A3的arduino code成一個,測試 **同步&燈號更新率** ::: --- ### 7. Final code :::success :bulb: **Notice:** 由於Nucleo-144在Arduino IDE上的timer interrupt以及輸出腳位不夠健全,為了能夠減少移植程式的時間,後來使用原本的Arduino mega2560做全燈條**同步&燈號更新率** ::: --- ### 7. Reference ##### 7-1. [arm MBED: NUCLEO-F412ZG](https:////os.mbed.com/platforms/ST-Nucleo-F412ZG/) ##### 7-2. STM32 Nucleo-144 user manual {%pdf https://www.farnell.com/datasheets/2014265.pdf %}