#pippitank制作の
前回: Gentoo/Neovimでの
今回は
+---------------+
| pippitank-cli |
+---------------+
| UART
+------+-----+ UART +---------+ PWM +-----+ +-------+
| pippitankd +------+ Arduino +-----+ ESC +-+ Motor |
+------------+ +---------+ +-----+ +-------+ラズパイには
Rpi -> ArduinoのL1500R1500(左ESCにpippitank-cli -> pippitankdの
初期状態では
sudo raspi-config3 Interface Optionsを

I6 Serial Port Enable/disableを

ログインシェルは

ハードウェアシリアルは

完了。

メモがてら
今回は
protocol.hはpollでtermiosのcfmakerawをread()が#include <stdio.h>
#include <getopt.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <poll.h>
#include "../protocol.h"
#define MAX_CLIENTS 1
#define BASE_POLL_FDS 2
#define BUFFER_SIZE 128
#define BAUDRATE B9600
#define PWM_MIN 1000
#define PWM_MAX 2000
#define PWM_IDLE 1500
int main(int argc, char *argv[])
{
int no_serial = 0;
char *serial_port_path = NULL;
int opt;
struct option options[] = {
{"port", required_argument, NULL, 'p'},
{"no-serial", no_argument, NULL, 'n'},
{0, 0, 0, 0 },
};
while ((opt = getopt_long(argc, argv, ":p:n", options, NULL)) != -1)
{
switch (opt)
{
case 'p':
serial_port_path = optarg;
break;
case 'n':
no_serial = 1;
break;
case ':':
fprintf(stderr, "Option -%c requires argument.\n", optopt);
return -1;
case '?':
fprintf(stderr, "Unknown option: -%c\n", optopt);
return -1;
}
}
// Set to -1 so poll() ignores this slot when --no-serial is given.
int serial_port = -1;
if (no_serial != 1)
{
if (serial_port_path == NULL)
{
fprintf(stderr, "[ERROR] Please specify the serial port path.\n");
return -1;
}
struct termios termios_options;
if ((serial_port = open(serial_port_path, O_RDWR | O_NOCTTY | O_NONBLOCK)) < 0)
{
fprintf(stderr, "[ERROR] Failed to open serial port: %s\n", serial_port_path);
return -1;
}
tcgetattr(serial_port, &termios_options);
cfsetispeed(&termios_options, BAUDRATE);
cfsetospeed(&termios_options, BAUDRATE);
// Needed because without raw mode, read() blocks until a newline,
// and Arduino's ACK has no terminator.
cfmakeraw(&termios_options);
tcsetattr(serial_port, TCSADRAIN, &termios_options);
}
// UNIX domain socket for accepting clients.
int server_fd;
struct sockaddr_un sock_addr;
if ((server_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("[ERROR] socket()");
return -1;
}
sock_addr.sun_family = AF_UNIX;
strcpy(sock_addr.sun_path, PIPPITANKD_SOCK_PATH);
// Remove any stale socket file.
unlink(PIPPITANKD_SOCK_PATH);
if (bind(server_fd, (struct sockaddr*)&sock_addr, sizeof(struct sockaddr_un)) == -1) {
perror("[ERROR] bind()");
return -1;
}
if (listen(server_fd, 5) == -1) {
perror("[ERROR] listen()");
return -1;
}
int poll_num_fds = BASE_POLL_FDS;
struct pollfd poll_fds[BASE_POLL_FDS + MAX_CLIENTS];
poll_fds[0].fd = server_fd;
poll_fds[0].events = POLLIN;
poll_fds[1].fd = serial_port;
poll_fds[1].events = POLLIN;
while (1)
{
if (poll(poll_fds, poll_num_fds, -1) < 0)
{
perror("[ERROR] poll()");
return -1;
}
// Accept incoming clients.
if (poll_fds[0].revents & POLLIN)
{
int client_fd = accept(server_fd, NULL, NULL);
if (client_fd < 0)
{
perror("[ERROR] accept()");
return -1;
}
if (poll_num_fds < BASE_POLL_FDS + MAX_CLIENTS)
{
printf("[INFO] Client connected: %d\n", client_fd);
poll_fds[poll_num_fds].fd = client_fd;
poll_fds[poll_num_fds].events = POLLIN;
poll_num_fds++;
}
else
{
close(client_fd);
}
}
// Handle responses from Arduino.
if (poll_fds[1].revents & POLLIN)
{
char buffer[4];
int n = read(serial_port, buffer, sizeof(buffer) - 1);
if (n > 0)
{
buffer[n] = '\0';
printf("[INFO] Response from Arduino (%d bytes): %s\n", n, buffer);
}
}
// Handle requests from clients.
for (int i = BASE_POLL_FDS; i < poll_num_fds; i++)
{
if (poll_fds[i].revents & POLLIN)
{
char buffer[BUFFER_SIZE];
int n = read(poll_fds[i].fd, buffer, sizeof(buffer) - 1);
if (n <= 0)
{
printf("[INFO] Client disconnected: %d\n", poll_fds[i].fd);
close(poll_fds[i].fd);
poll_fds[i] = poll_fds[poll_num_fds - 1];
poll_num_fds--;
i--; // Re-check this slot — it now holds the swapped-in last entry.
}
else
{
int cmd_value, pwm_value, ret;
char cmd_type;
char cmd_str[BUFFER_SIZE] = "";
buffer[n] = '\0';
ret = sscanf(buffer, "%c %d", &cmd_type, &cmd_value);
if (ret != 2)
{
continue;
}
switch (cmd_type)
{
case 'F':
if (cmd_value >= 0 && cmd_value <= 100)
{
pwm_value = PWM_IDLE + (PWM_MAX - PWM_IDLE) * cmd_value / 100;
sprintf(cmd_str, "L%d", pwm_value);
}
break;
case 'B':
if (cmd_value >= 0 && cmd_value <= 100)
{
pwm_value = PWM_IDLE - (PWM_IDLE - PWM_MIN) * cmd_value / 100;
sprintf(cmd_str, "L%d", pwm_value);
}
break;
}
if (cmd_str[0] == '\0')
{
continue;
}
if (no_serial != 1)
{
write(serial_port, cmd_str, strlen(cmd_str));
write(serial_port, "\n", 1);
printf("[INFO] Command sent: %s\n", cmd_str);
}
else
{
printf("[INFO] Command skipped: %s\n", cmd_str);
}
}
}
}
}
close(server_fd);
unlink(PIPPITANKD_SOCK_PATH);
return 0;
}#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include "../protocol.h"
#define BUFFER_SIZE 128
// A simple pippitankd client.
// Sends whatever you type, except "q" which quits the program.
int main(int argc, char *argv[])
{
int server_fd;
struct sockaddr_un sock_addr;
if ((server_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
{
perror("ERROR: socket()");
return -1;
}
sock_addr.sun_family = AF_UNIX;
strcpy(sock_addr.sun_path, PIPPITANKD_SOCK_PATH);
if (connect(server_fd, (struct sockaddr*)&sock_addr, sizeof(struct sockaddr_un)) == -1)
{
perror("ERROR: connect()");
return -1;
}
char buffer[BUFFER_SIZE];
while (1)
{
printf("> ");
fflush(stdout);
if (fgets(buffer, sizeof(buffer), stdin) == NULL)
{
break;
}
buffer[strcspn(buffer, "\n")] = '\0';
if (strlen(buffer) == 0)
{
continue;
}
if (strcmp(buffer, "q") == 0)
{
break;
}
write(server_fd, buffer, strlen(buffer));
}
printf("Bye...");
close(server_fd);
return 0;
}#include <SoftwareSerial.h>
#include <Servo.h>
const byte rxPin = 2;
const byte txPin = 3;
const byte pwmLeftPin = 5;
const unsigned int PWM_MIN = 1000;
const unsigned int PWM_MAX = 2000;
const unsigned int PWM_IDLE = 1500;
SoftwareSerial pippiSerial (rxPin, txPin);
Servo pippiServoLeft;
void setup()
{
pippiSerial.begin(9600);
pippiServoLeft.attach(pwmLeftPin, PWM_MIN, PWM_MAX);
pippiServoLeft.writeMicroseconds(PWM_IDLE);
}
void loop()
{
if (pippiSerial.available() > 0)
{
char c = pippiSerial.read();
if (c == 'L')
{
int pulseLeft = pippiSerial.parseInt();
if (pulseLeft <= PWM_MAX && pulseLeft >= PWM_MIN)
{
pippiServoLeft.writeMicroseconds(pulseLeft);
pippiSerial.print("A");
}
}
}
}PWMの
arduino-cli lib install Servoラズパイに
Description : Raspberry Pi Zero W rev 1.1
Revision : 9000c1
SoC : BCM2835
RAM : 512MB
Storage : MicroSD
USB ports : 1 (of which 0 USB3)
Ethernet ports : 0 (0Mbps max. speed)
Wi-fi : True
Bluetooth : True
Camera ports (CSI) : 1
Display ports (DSI): 0
,--oooooooooooooooooooo---.
| 1ooooooooooooooooooo J8|
---+ PiZero W RUN o1 c|
sd| V1.1 +---+ TV 1o s|
---+ |SoC| i|
| hdmi +---+ usb pwr |
`-| |------------| |-| |-'
J8:
3V3 (1) (2) 5V
GPIO2 (3) (4) 5V
GPIO3 (5) (6) GND
GPIO4 (7) (8) GPIO14
GND (9) (10) GPIO15
GPIO17 (11) (12) GPIO18
GPIO27 (13) (14) GND
GPIO22 (15) (16) GPIO23
3V3 (17) (18) GPIO24
GPIO10 (19) (20) GND
GPIO9 (21) (22) GPIO25
GPIO11 (23) (24) GPIO8
GND (25) (26) GPIO7
GPIO0 (27) (28) GPIO1
GPIO5 (29) (30) GND
GPIO6 (31) (32) GPIO12
GPIO13 (33) (34) GND
GPIO19 (35) (36) GPIO16
GPIO26 (37) (38) GPIO20
GND (39) (40) GPIO21
RUN:
RUN (1)
GND (2)
TV:
COMPOSITE (1)
GND (2)
For further information, please refer to https://pinout.xyz/